diff --git a/Makefile.work b/Makefile.work index 4c3c75cb7a1d..5b143f7bbf77 100644 --- a/Makefile.work +++ b/Makefile.work @@ -10,6 +10,7 @@ # * ENABLE_ZTP: Enables zero touch provisioning. # * SHUTDOWN_BGP_ON_START: Sets admin-down state for all bgp peerings after restart. # * INCLUDE_KUBERNETES: Allows including Kubernetes +# * INCLUDE_MUX: Include MUX feature/services for TOR switch. # * ENABLE_PFCWD_ON_START: Enable PFC Watchdog (PFCWD) on server-facing ports # * by default for TOR switch. # * ENABLE_SYNCD_RPC: Enables rpc-based syncd builds. @@ -243,6 +244,7 @@ SONIC_BUILD_INSTRUCTION := make \ SONIC_INCLUDE_SYSTEM_TELEMETRY=$(INCLUDE_SYSTEM_TELEMETRY) \ SONIC_INCLUDE_RESTAPI=$(INCLUDE_RESTAPI) \ SONIC_INCLUDE_ACMS=$(INCLUDE_ACMS) \ + SONIC_INCLUDE_MUX=$(INCLUDE_MUX) \ TELEMETRY_WRITABLE=$(TELEMETRY_WRITABLE) \ EXTRA_DOCKER_TARGETS=$(EXTRA_DOCKER_TARGETS) \ BUILD_LOG_TIMESTAMP=$(BUILD_LOG_TIMESTAMP) \ diff --git a/dockers/docker-mux/Dockerfile.j2 b/dockers/docker-mux/Dockerfile.j2 new file mode 100755 index 000000000000..66ec315f0809 --- /dev/null +++ b/dockers/docker-mux/Dockerfile.j2 @@ -0,0 +1,36 @@ +{% from "dockers/dockerfile-macros.j2" import install_debian_packages, install_python_wheels, copy_files %} +FROM docker-config-engine-buster + +ARG docker_container_name +RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name#%syslogtag%/;" /etc/rsyslog.conf + +## Make apt-get non-interactive +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -f -y \ + libmnl0 + +{% if docker_mux_debs.strip() -%} +# Copy locally-built Debian package dependencies +{{ copy_files("debs/", docker_mux_debs.split(' '), "/debs/") }} + +# Install locally-built Debian packages and implicitly install their dependencies +{{ install_debian_packages(docker_mux_debs.split(' ')) }} +{%- endif %} + +## Clean up +RUN apt-get clean -y && \ + apt-get autoclean -y && \ + apt-get autoremove -y && \ + rm -rf /debs + +COPY ["docker-init.sh", "/usr/bin/"] +COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] +COPY ["files/supervisor-proc-exit-listener", "/usr/bin"] +COPY ["critical_processes", "/etc/supervisor/"] + +## Copy all Jinja2 template files into the templates folder +COPY ["*.j2", "/usr/share/sonic/templates/"] + +ENTRYPOINT ["/usr/bin/docker-init.sh"] diff --git a/dockers/docker-mux/critical_processes b/dockers/docker-mux/critical_processes new file mode 100644 index 000000000000..eb2568f1ef2b --- /dev/null +++ b/dockers/docker-mux/critical_processes @@ -0,0 +1 @@ +program:linkmgrd diff --git a/dockers/docker-mux/docker-init.sh b/dockers/docker-mux/docker-init.sh new file mode 100755 index 000000000000..bea1686132a1 --- /dev/null +++ b/dockers/docker-mux/docker-init.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +# Generate supervisord config file +mkdir -p /etc/supervisor/conf.d/ + +# The docker container should start this script as PID 1, so now that supervisord is +# properly configured, we exec supervisord so that it runs as PID 1 for the +# duration of the container's lifetime +exec /usr/local/bin/supervisord diff --git a/dockers/docker-mux/supervisord.conf b/dockers/docker-mux/supervisord.conf new file mode 100644 index 000000000000..242345e10db5 --- /dev/null +++ b/dockers/docker-mux/supervisord.conf @@ -0,0 +1,41 @@ +[supervisord] +logfile_maxbytes=1MB +logfile_backups=2 +nodaemon=true + +[eventlistener:dependent-startup] +command=python3 -m supervisord_dependent_startup +autostart=true +autorestart=unexpected +startretries=0 +exitcodes=0,3 +events=PROCESS_STATE +buffer_size=100 + +[eventlistener:supervisor-proc-exit-listener] +command=/usr/bin/supervisor-proc-exit-listener --container-name mux +events=PROCESS_STATE_EXITED,PROCESS_STATE_RUNNING +autostart=true +autorestart=unexpected + +[program:rsyslogd] +command=/usr/sbin/rsyslogd -n -iNONE +priority=1 +autostart=false +autorestart=unexpected +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true + +[program:linkmgrd] +command=nice -n -20 /usr/sbin/linkmgrd -l /usr/share/sonic/templates/mux_config.json -v info +priority=2 +autostart=false +autorestart=false +startsecs=0 +startretries=0 +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for=rsyslogd:running + diff --git a/files/build_templates/init_cfg.json.j2 b/files/build_templates/init_cfg.json.j2 index 0281213e1b16..e3b7071eb4f0 100644 --- a/files/build_templates/init_cfg.json.j2 +++ b/files/build_templates/init_cfg.json.j2 @@ -31,6 +31,7 @@ {%- if sonic_asic_platform == "vs" %}{% do features.append(("gbsyncd", "enabled", false, "enabled")) %}{% endif %} {%- if include_iccpd == "y" %}{% do features.append(("iccpd", "disabled", false, "enabled")) %}{% endif %} {%- if include_mgmt_framework == "y" %}{% do features.append(("mgmt-framework", "enabled", true, "enabled")) %}{% endif %} +{%- if include_mux == "y" %}{% do features.append(("mux", "enabled", false, "enabled")) %}{% endif %} {%- if include_nat == "y" %}{% do features.append(("nat", "disabled", false, "enabled")) %}{% endif %} {%- if include_restapi == "y" %}{% do features.append(("restapi", "enabled", false, "enabled")) %}{% endif %} {%- if include_sflow == "y" %}{% do features.append(("sflow", "disabled", false, "enabled")) %}{% endif %} diff --git a/files/build_templates/mux.service.j2 b/files/build_templates/mux.service.j2 new file mode 100644 index 000000000000..72a5925e13ba --- /dev/null +++ b/files/build_templates/mux.service.j2 @@ -0,0 +1,17 @@ +[Unit] +Description=MUX Cable Container +Requires=database.service updategraph.service pmon.service swss.service +After=pmon.service swss.service +StartLimitIntervalSec=1200 +StartLimitBurst=3 + +[Service] +User={{ sonicadmin_user }} +ExecStartPre=/usr/bin/{{docker_container_name}}.sh start +ExecStart=/usr/bin/{{docker_container_name}}.sh wait +ExecStop=/usr/bin/{{docker_container_name}}.sh stop +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/rules/config b/rules/config index bbf5e66bb71c..ae010ce6c895 100644 --- a/rules/config +++ b/rules/config @@ -188,3 +188,6 @@ SONIC_VERSION_CONTROL_COMPONENTS ?= none # ENABLE_DOCKER_BASE_PULL = y REGISTRY_PORT=443 REGISTRY_SERVER=sonicdev-microsoft.azurecr.io + +# INCLUDE_MUX - build docker-mux for dual ToR (Gemini) +INCLUDE_MUX = y diff --git a/rules/docker-mux.dep b/rules/docker-mux.dep new file mode 100644 index 000000000000..70d4dd4edf89 --- /dev/null +++ b/rules/docker-mux.dep @@ -0,0 +1,12 @@ + +DPATH := $($(DOCKER_MUX)_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/docker-mux.mk rules/docker-mux.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(DPATH)) + +$(DOCKER_MUX)_CACHE_MODE := GIT_CONTENT_SHA +$(DOCKER_MUX)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(DOCKER_MUX)_DEP_FILES := $(DEP_FILES) + +$(eval $(call add_dbg_docker,$(DOCKER_MUX),$(DOCKER_MUX_DBG))) + diff --git a/rules/docker-mux.mk b/rules/docker-mux.mk new file mode 100644 index 000000000000..38b1e972453d --- /dev/null +++ b/rules/docker-mux.mk @@ -0,0 +1,32 @@ +# Docker image for MUX + +DOCKER_MUX_STEM = docker-mux +DOCKER_MUX = $(DOCKER_MUX_STEM).gz +DOCKER_MUX_DBG = $(DOCKER_MUX_STEM)-$(DBG_IMAGE_MARK).gz + +$(DOCKER_MUX)_PATH = $(DOCKERS_PATH)/$(DOCKER_MUX_STEM) + +$(DOCKER_MUX)_DEPENDS = $(SONIC_LINKMGRD) $(LIBSWSSCOMMON) $(LIBHIREDIS) +$(DOCKER_MUX)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_DEPENDS) +$(DOCKER_MUX)_DBG_DEPENDS += $(SONIC_LINKMGRD_DBG) $(LIBSWSSCOMMON_DBG) $(LIBHIREDIS_DBG) + +$(DOCKER_MUX)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES) + +$(DOCKER_MUX)_LOAD_DOCKERS = $(DOCKER_CONFIG_ENGINE_BUSTER) + +ifeq ($(INCLUDE_MUX), y) +SONIC_DOCKER_IMAGES += $(DOCKER_MUX) +SONIC_INSTALL_DOCKER_IMAGES += $(DOCKER_MUX) +endif + +ifeq ($(INCLUDE_MUX), y) +SONIC_DOCKER_DBG_IMAGES += $(DOCKER_MUX_DBG) +SONIC_INSTALL_DOCKER_DBG_IMAGES += $(DOCKER_MUX_DBG) +endif + +$(DOCKER_MUX)_CONTAINER_NAME = mux +$(DOCKER_MUX)_RUN_OPT += --privileged -t +$(DOCKER_MUX)_RUN_OPT += -v /etc/sonic:/etc/sonic:ro +$(DOCKER_ORCHAGENT)_RUN_OPT += -v /var/log/mux:/var/log/mux:rw +$(DOCKER_MUX)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT) + diff --git a/rules/linkmgrd.dep b/rules/linkmgrd.dep new file mode 100644 index 000000000000..d94e328bf399 --- /dev/null +++ b/rules/linkmgrd.dep @@ -0,0 +1,9 @@ + +SPATH := $($(SONIC_LINKMGRD)_SRC_PATH) +DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/linkmgrd.mk rules/linkmgrd.dep +DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST) +DEP_FILES += $(shell git ls-files $(SPATH)) + +$(SONIC_LINKMGRD)_CACHE_MODE := GIT_CONTENT_SHA +$(SONIC_LINKMGRD)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) +$(SONIC_LINKMGRD)_DEP_FILES := $(DEP_FILES) diff --git a/rules/linkmgrd.mk b/rules/linkmgrd.mk new file mode 100644 index 000000000000..ed1bd4071fe7 --- /dev/null +++ b/rules/linkmgrd.mk @@ -0,0 +1,18 @@ +# SONiC LINK ManaGeR Daemon package + +SONIC_LINKMGRD_VERSION = 1.0.0-1 +SONIC_LINKMGRD_PKG_NAME = linkmgrd + +export SONIC_LINKMGRD_VERSION SONIC_LINKMGRD_PKG_NAME + +SONIC_LINKMGRD = sonic-$(SONIC_LINKMGRD_PKG_NAME)_$(SONIC_LINKMGRD_VERSION)_$(CONFIGURED_ARCH).deb +$(SONIC_LINKMGRD)_SRC_PATH = $(SRC_PATH)/$(SONIC_LINKMGRD_PKG_NAME) +$(SONIC_LINKMGRD)_DEPENDS = $(LIBSWSSCOMMON_DEV) $(LIBSWSSCOMMON) $(LIBHIREDIS_DEV) $(LIBHIREDIS) + +SONIC_DPKG_DEBS += $(SONIC_LINKMGRD) + +SONIC_LINKMGRD_DBG = sonic-$(SONIC_LINKMGRD_PKG_NAME)-dbgsym_$(SONIC_LINKMGRD_VERSION)_$(CONFIGURED_ARCH).deb +$(SONIC_LINKMGRD)_DBG_DEPENDS = $(LIBSWSSCOMMON_DEV) $(LIBSWSSCOMMON_DBG) $(LIBHIREDIS_DEV) $(LIBHIREDIS_DBG) +$(eval $(call add_derived_package,$(SONIC_LINKMGRD),$(SONIC_LINKMGRD_DBG))) + +export SONIC_LINKMGRD SONIC_LINKMGRD_DBG diff --git a/slave.mk b/slave.mk index 4156f53c3b67..354375fa27da 100644 --- a/slave.mk +++ b/slave.mk @@ -142,6 +142,10 @@ ifeq ($(SONIC_INCLUDE_NAT),y) INCLUDE_NAT = y endif +ifeq ($(SONIC_INCLUDE_MUX),y) +INCLUDE_MUX = y +endif + include $(RULES_PATH)/functions @@ -257,6 +261,7 @@ $(info "INCLUDE_RESTAPI" : "$(INCLUDE_RESTAPI)") $(info "INCLUDE_SFLOW" : "$(INCLUDE_SFLOW)") $(info "INCLUDE_NAT" : "$(INCLUDE_NAT)") $(info "INCLUDE_KUBERNETES" : "$(INCLUDE_KUBERNETES)") +$(info "INCLUDE_MUX" : "$(INCLUDE_MUX)") $(info "TELEMETRY_WRITABLE" : "$(TELEMETRY_WRITABLE)") $(info "PDDF_SUPPORT" : "$(PDDF_SUPPORT)") $(info ) @@ -929,6 +934,7 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ export python_swss_debs+=" $(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(LIBSWSSCOMMON)) $(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(PYTHON_SWSSCOMMON)) $(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(PYTHON3_SWSSCOMMON))" export sonic_utilities_py3_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_UTILITIES_PY3))" export sonic_host_services_py3_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_HOST_SERVICES_PY3))" + export include_mux="$(INCLUDE_MUX)" $(foreach docker, $($*_DOCKERS),\ export docker_image="$(docker)" diff --git a/src/linkmgrd/.gitignore b/src/linkmgrd/.gitignore new file mode 100644 index 000000000000..0084d98fa410 --- /dev/null +++ b/src/linkmgrd/.gitignore @@ -0,0 +1,7 @@ +**/*.o +**/*.d +debian/debhelper-build-stamp +debian/sonic-linkmgrd/ +linkmgrd +linkmgrd-test + diff --git a/src/linkmgrd/Makefile b/src/linkmgrd/Makefile new file mode 100644 index 000000000000..8587a67070bd --- /dev/null +++ b/src/linkmgrd/Makefile @@ -0,0 +1,92 @@ +-include ../makefile.init + +RM := rm -rf +LINKMGRD_TARGET := linkmgrd +LINKMGRD_TEST_TARGET := linkmgrd-test +CP := cp +MKDIR := mkdir +CC := g++ +MV := mv +CPP_FLAGS := -O3 -Wall -c -fmessage-length=0 -fPIC +TOPDIR := $(dir $(firstword $(MAKEFILE_LIST))) + +INCLUDES := \ + -I"$(TOPDIR)/src" \ + -I"/usr/include/libnl3/" + +# All of the sources participating in the build are defined here +-include sources.mk +-include test/subdir.mk +-include src/mux_state/subdir.mk +-include src/link_state/subdir.mk +-include src/link_prober/subdir.mk +-include src/link_manager/subdir.mk +-include src/common/subdir.mk +-include src/subdir.mk +-include subdir.mk +-include objects.mk + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(strip $(CC_DEPS)),) +-include $(CC_DEPS) +endif +ifneq ($(strip $(C++_DEPS)),) +-include $(C++_DEPS) +endif +ifneq ($(strip $(C_UPPER_DEPS)),) +-include $(C_UPPER_DEPS) +endif +ifneq ($(strip $(CXX_DEPS)),) +-include $(CXX_DEPS) +endif +ifneq ($(strip $(CPP_DEPS)),) +-include $(CPP_DEPS) +endif +ifneq ($(strip $(C_DEPS)),) +-include $(C_DEPS) +endif +endif + +-include ../makefile.defs + +# Add inputs and outputs from these tool invocations to the build variables + +# All Target +all: sonic-linkmgrd + +# Tool invocations +sonic-linkmgrd: $(OBJS) $(USER_OBJS) $(OBJS_LINKMGRD) + @echo 'Building target: $@' + @echo 'Invoking: GCC C++ Linker' + $(CC) -pthread -o "$(LINKMGRD_TARGET)" $(OBJS) $(OBJS_LINKMGRD) $(USER_OBJS) $(LIBS) + @echo 'Finished building target: $@' + @echo ' ' + +# Other Targets +test: $(OBJS) $(USER_OBJS) $(OBJS_LINKMGRD_TEST) + @echo 'Building target: $@' + @echo 'Invoking: GCC C++ Linker' + $(CC) -pthread -o "$(LINKMGRD_TEST_TARGET)" $(OBJS) $(OBJS_LINKMGRD_TEST) $(USER_OBJS) $(LIBS) $(LIBS_TEST) + @echo 'Executing test target: $@' + LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.5 ./$(LINKMGRD_TEST_TARGET) + @echo 'Finished building target: $@' + @echo ' ' + +install: + $(MKDIR) -p $(DESTDIR)/usr/sbin + $(MV) $(LINKMGRD_TARGET) $(DESTDIR)/usr/sbin + $(RM) $(CC_DEPS) $(C++_DEPS) $(EXECUTABLES) $(C_UPPER_DEPS) $(CXX_DEPS) $(OBJS) $(CPP_DEPS) $(C_DEPS) \ + $(LINKMGRD_TARGET) $(LINKMGRD_TEST_TARGET) $(OBJS_LINKMGRD) $(OBJS_LINKMGRD_TEST) + +deinstall: + $(RM) $(DESTDIR)/usr/sbin/$(LINKMGRD_TARGET) + $(RM) -rf $(DESTDIR)/usr/sbin + +clean: + -$(RM) $(CC_DEPS) $(C++_DEPS) $(EXECUTABLES) $(C_UPPER_DEPS) $(CXX_DEPS) $(OBJS) $(CPP_DEPS) $(C_DEPS) \ + $(LINKMGRD_TARGET) $(LINKMGRD_TEST_TARGET) $(OBJS_LINKMGRD) $(OBJS_LINKMGRD_TEST) + -@echo ' ' + +.PHONY: all clean dependents + +-include ../makefile.targets diff --git a/src/linkmgrd/debian/changelog b/src/linkmgrd/debian/changelog new file mode 100644 index 000000000000..0b89b2123a9f --- /dev/null +++ b/src/linkmgrd/debian/changelog @@ -0,0 +1,5 @@ +sonic-linkmgrd (1.0.0-1) UNRELEASED; urgency=medium + + * Initial release. + + -- Tamer Ahmed Mon, 26 Oct 2020 12:00:00 -0700 diff --git a/src/linkmgrd/debian/compat b/src/linkmgrd/debian/compat new file mode 100644 index 000000000000..f599e28b8ab0 --- /dev/null +++ b/src/linkmgrd/debian/compat @@ -0,0 +1 @@ +10 diff --git a/src/linkmgrd/debian/control b/src/linkmgrd/debian/control new file mode 100644 index 000000000000..fbe3f67779d0 --- /dev/null +++ b/src/linkmgrd/debian/control @@ -0,0 +1,19 @@ +Source: sonic-linkmgrd +Section: devel +Priority: optional +Maintainer: Tamer Ahmed +Build-Depends: debhelper (>= 8.0.0), + dh-systemd +Standards-Version: 3.9.3 +Homepage: https://github.com/Azure/sonic-buildimage +XS-Go-Import-Path: github.com/Azure/sonic-buildimage + +Package: sonic-linkmgrd +Architecture: any +Built-Using: ${misc:Built-Using} +Depends: libboost-program-options1.71.0, + libboost-system1.71.0, + libboost-thread1.71.0, + libboost-date-time1.71.0, + libboost-log1.71.0 +Description: SONiC LINK ManaGeR Daemon (linkmgrd) diff --git a/src/linkmgrd/debian/rules b/src/linkmgrd/debian/rules new file mode 100755 index 000000000000..60a99a3a8fc6 --- /dev/null +++ b/src/linkmgrd/debian/rules @@ -0,0 +1,6 @@ +#!/usr/bin/make -f + +%: + dh $@ --parallel + + \ No newline at end of file diff --git a/src/linkmgrd/objects.mk b/src/linkmgrd/objects.mk new file mode 100644 index 000000000000..d93f94f11085 --- /dev/null +++ b/src/linkmgrd/objects.mk @@ -0,0 +1,17 @@ + +USER_OBJS := + +LIBS := \ + -lswsscommon \ + -lboost_system \ + -lboost_date_time \ + -lboost_thread \ + -lboost_log \ + -lboost_log_setup \ + -lboost_program_options \ + -lnl-3 \ + -lnl-route-3 + +LIBS_TEST := \ + -lgtest_main \ + -lgtest diff --git a/src/linkmgrd/sources.mk b/src/linkmgrd/sources.mk new file mode 100644 index 000000000000..e15b1077b8f8 --- /dev/null +++ b/src/linkmgrd/sources.mk @@ -0,0 +1,30 @@ + +C_UPPER_SRCS := +CXX_SRCS := +C++_SRCS := +OBJ_SRCS := +CC_SRCS := +ASM_SRCS := +CPP_SRCS := +C_SRCS := +O_SRCS := +S_UPPER_SRCS := +CC_DEPS := +C++_DEPS := +EXECUTABLES := +C_UPPER_DEPS := +CXX_DEPS := +OBJS := +CPP_DEPS := +C_DEPS := + +# Every subdirectory with source files must be described here +SUBDIRS := \ + src \ + src/common \ + src/link_manager \ + src/link_prober \ + src/link_state \ + src/mux_state \ + test \ + diff --git a/src/linkmgrd/src/DbInterface.cpp b/src/linkmgrd/src/DbInterface.cpp new file mode 100644 index 000000000000..9082222f4983 --- /dev/null +++ b/src/linkmgrd/src/DbInterface.cpp @@ -0,0 +1,572 @@ +/* + * DbInterface.cpp + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#include +#include + +#include +#include +#include + +#include "swss/netdispatcher.h" +#include "swss/netlink.h" +#include "swss/select.h" + +#include "DbInterface.h" +#include "MuxManager.h" +#include "common/MuxLogger.h" +#include "common/MuxException.h" +#include "NetMsgInterface.h" + +namespace mux +{ +constexpr auto DEFAULT_TIMEOUT_MSEC = 1000; +std::vector DbInterface::mMuxState = {"active", "standby", "unknown"}; + +// +// ---> DbInterface(mux::MuxManager *muxManager); +// +// class constructor +// +DbInterface::DbInterface(mux::MuxManager *muxManager, boost::asio::io_service *ioService) : + mMuxManagerPtr(muxManager), + mStrand(*ioService) +{ +} + + +// +// ---> getMuxState(const std::string &portName); +// +// retrieve the current MUX state +// +void DbInterface::getMuxState(const std::string &portName) +{ + MUXLOGDEBUG(portName); + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &DbInterface::handleGetMuxState, + this, + portName + ))); +} + +// +// ---> setMuxState(const std::string &portName, mux_state::MuxState::Label label); +// +// set MUX state in APP DB for orchagent processing +// +void DbInterface::setMuxState(const std::string &portName, mux_state::MuxState::Label label) +{ + MUXLOGINFO(boost::format("%s: setting mux to %s") % portName % mMuxState[label]); + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &DbInterface::handleSetMuxState, + this, + portName, + label + ))); +} + +// +// ---> probeMuxState(const std::string &portName) +// +// trigger xcvrd to read MUX state using i2c +// +void DbInterface::probeMuxState(const std::string &portName) +{ + MUXLOGINFO(portName); + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &DbInterface::handleProbeMuxState, + this, + portName + ))); +} + +// +// ---> initialize(); +// +// initialize DB tables and start SWSS listening thread +// +void DbInterface::initialize() +{ + try { + mAppDbPtr = std::make_shared ("APPL_DB", 0); + mStateDbPtr = std::make_shared ("STATE_DB", 0); + + mAppDbMuxTablePtr = std::make_shared ( + mAppDbPtr.get(), APP_MUX_CABLE_TABLE_NAME + ); + mAppDbMuxCommandTablePtr = std::make_shared ( + mAppDbPtr.get(), APP_MUX_CABLE_COMMAND_TABLE_NAME + ); + mStateDbMuxTablePtr = std::make_shared ( + mStateDbPtr.get(), STATE_MUX_CABLE_TABLE_NAME + ); + mMuxStateTablePtr = std::make_shared (mStateDbPtr.get(), STATE_MUX_CABLE_TABLE_NAME); + + mSwssThreadPtr = std::make_shared (&DbInterface::handleSwssNotification, this); + } + catch (const std::bad_alloc& ex) { + std::ostringstream errMsg; + errMsg << "Failed allocate memory. Exception details: " << ex.what(); + + throw MUX_ERROR(BadAlloc, errMsg.str()); + } +} + +// +// ---> updateServerMacAddress(boost::asio::ip::address serverIp, uint8_t *serverMac); +// +// Update Server MAC address behind a MUX port +// +void DbInterface::updateServerMacAddress(boost::asio::ip::address serverIp, const uint8_t *serverMac) +{ + MUXLOGDEBUG(boost::format("server IP: %s") % serverIp.to_string()); + + ServerIpPortMap::const_iterator cit = mServerIpPortMap.find(serverIp); + if (cit != mServerIpPortMap.cend()) { + std::array macAddress; + + memcpy(macAddress.data(), serverMac, macAddress.size()); + + mMuxManagerPtr->processGetServerMacAddress(cit->second, macAddress); + } +} + +// +// ---> handleGetMuxState(const std::string portName); +// +// get state db MUX state +// +void DbInterface::handleGetMuxState(const std::string portName) +{ + MUXLOGDEBUG(portName); + + std::string state; + if (mMuxStateTablePtr->hget(portName, "state", state)) { + mMuxManagerPtr->processGetMuxState(portName, state); + } +} + +// +// ---> handleSetMuxState(const std::string portName, mux_state::MuxState::Label label); +// +// set MUX state in APP DB for orchagent processing +// +void DbInterface::handleSetMuxState(const std::string portName, mux_state::MuxState::Label label) +{ + MUXLOGINFO(boost::format("%s: setting mux state to %s") % portName % mMuxState[label]); + + if (label <= mux_state::MuxState::Unknown) { + std::vector values = { + {"state", mMuxState[label]}, + }; + mAppDbMuxTablePtr->set(portName, values); + } +} + +// +// ---> handleProbeMuxState(const std::string portName) +// +// trigger xcvrd to read MUX state using i2c +// +void DbInterface::handleProbeMuxState(const std::string portName) +{ + MUXLOGINFO(portName); + + mAppDbMuxCommandTablePtr->hset(portName, "command", "probe"); +} + +// +// ---> getLoopback2InterfaceInfo( +// std::shared_ptr configDbConnector, +// std::shared_ptr stateDbConnector +// ); +// +// retrieve Loopback2 interface information and block until it shows as OK in the state db +// +void DbInterface::getLoopback2InterfaceInfo( + std::shared_ptr configDbConnector, + std::shared_ptr stateDbConnector +) +{ + MUXLOGINFO("Reading Loopback2 interface information"); + std::string loopback2 = "Loopback2|"; + swss::Table configDbLoopbackTable(configDbConnector.get(), CFG_LOOPBACK_INTERFACE_TABLE_NAME); + swss::Table stateDbInterfaceTable(stateDbConnector.get(), STATE_INTERFACE_TABLE_NAME); + std::string loopback2IntfKey; + std::vector loopbackIntfs; + configDbLoopbackTable.getKeys(loopbackIntfs); + for (auto &loopbackIntf: loopbackIntfs) { + size_t pos = loopbackIntf.find(loopback2); + if (pos != std::string::npos) { + std::string ip = loopbackIntf.substr(loopback2.size(), loopbackIntf.size() - loopback2.size()); + MUXLOGINFO(boost::format("configDb Loopback2: ip: %s") % ip); + + pos = ip.find("/"); + if (pos != std::string::npos) { + ip.erase(pos); + } + boost::system::error_code errorCode; + boost::asio::ip::address ipAddress = boost::asio::ip::make_address(ip, errorCode); + if (!errorCode) { + if (ipAddress.is_v4()) { + mMuxManagerPtr->setLoopbackIpv4Address(ipAddress); + loopback2IntfKey = loopbackIntf; + } + else if (ipAddress.is_v6()) { + // handle IPv6 probing + } + } else { + MUXLOGFATAL(boost::format("Received Loopback2 IP: %s, error code: %d") % ip % errorCode); + } + } + } + + while (true) { + std::vector fieldValues; + stateDbInterfaceTable.get(loopback2IntfKey, fieldValues); + 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; + + MUXLOGINFO(boost::format("key: %s: f: %s, v: %s") % loopback2IntfKey % f % v); + if (v == "ok") { + break; + } + + boost::this_thread::sleep(boost::posix_time::milliseconds(DEFAULT_TIMEOUT_MSEC)); + } + } +} + +// +// ---> getServerIpAddress(std::shared_ptr configDbConnector); +// +// retrieve server/blades IP address and builds a map of IP to port name +// +void DbInterface::getServerIpAddress(std::shared_ptr configDbConnector) +{ + MUXLOGINFO("Reading MUX Server IPs"); + swss::Table configDbMuxCableTable(configDbConnector.get(), CFG_MUX_CABLE_TABLE_NAME); + std::vector entries; + + configDbMuxCableTable.getContent(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) == "server_ipv4";} + ); + if (cit != fieldValues.cend()) { + const std::string f = cit->first; + std::string smartNicIpAddress = cit->second; + + MUXLOGDEBUG(boost::format("port: %s, %s = %s") % portName % f % smartNicIpAddress); + + size_t pos = smartNicIpAddress.find("/"); + if (pos != std::string::npos) { + smartNicIpAddress.erase(pos); + } + + boost::system::error_code errorCode; + boost::asio::ip::address ipAddress = boost::asio::ip::make_address(smartNicIpAddress, errorCode); + if (!errorCode) { + mMuxManagerPtr->addOrUpdateMuxPort(portName, ipAddress); + mServerIpPortMap[ipAddress] = portName; + } else { + MUXLOGFATAL(boost::format("%s: Received invalid server IP: %s, error code: %d") % + portName % + smartNicIpAddress % + errorCode + ); + } + } + } +} + +// +// ---> handleMuxPortConfigNotifiction(swss::SubscriberStateTable &configMuxTable); +// +// handles MUX port configuration change notification +// +void DbInterface::handleMuxPortConfigNotifiction(swss::SubscriberStateTable &configMuxTable) +{ + std::deque entries; + + configMuxTable.pops(entries); + for (auto &entry: entries) { + std::string port = 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) == "state";} + ); + if (cit != fieldValues.cend()) { + const std::string f = cit->first; + const std::string v = cit->second; + + MUXLOGDEBUG(boost::format("key: %s, Operation: %s, f: %s, v: %s") % + port % + operation % + f % + v + ); + mMuxManagerPtr->updateMuxPortConfig(port, v); + } + } +} + +// +// ---> handleLocalhostConfigNotifiction(swss::SubscriberStateTable &configLocalhostTable); +// +// handles localhost configuration change notification +// +void DbInterface::handleLocalhostConfigNotifiction(swss::SubscriberStateTable &configLocalhostTable) +{ + std::deque entries; + + configLocalhostTable.pops(entries); + for (auto &entry: entries) { + std::string key = kfvKey(entry); + if (key == "LINK_PROBE") { + std::string operation = kfvOp(entry); + std::vector fieldValues = kfvFieldsValues(entry); + + for (auto &fieldValue: fieldValues) { + std::string f = fvField(fieldValue); + std::string v = fvValue(fieldValue); + if (f == "interval") { + mMuxManagerPtr->setTimeoutIpv4_msec(boost::lexical_cast (v)); + } else if (f == "interval_for_v6") { + mMuxManagerPtr->setTimeoutIpv6_msec(boost::lexical_cast (v)); + } else if (f == "timeout") { + mMuxManagerPtr->setStateChangeRetryCount(boost::lexical_cast (v)); + } else if (f == "suspend_timer") { + mMuxManagerPtr->setSuspendTimeout_msec(boost::lexical_cast (v)); + } + + MUXLOGINFO(boost::format("key: %s, Operation: %s, f: %s, v: %s") % + key % + operation % + f % + v + ); + } + } + } +} + +// +// ---> handleLinkStateNotifiction(swss::SubscriberStateTable &appdbPortTable); +// +// handles link state change notification +// +void DbInterface::handleLinkStateNotifiction(swss::SubscriberStateTable &appdbPortTable) +{ + std::deque entries; + + appdbPortTable.pops(entries); + for (auto &entry: entries) { + std::string port = 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) == "oper_status";} + ); + 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 % + operation % + f % + v + ); + mMuxManagerPtr->addOrUpdateMuxPortLinkState(port, v); + } + } +} + +// +// ---> handleMuxResponseNotifiction(swss::SubscriberStateTable &appdbPortTable); +// +// handles MUX response (from xcvrd) notification +// +void DbInterface::handleMuxResponseNotifiction(swss::SubscriberStateTable &appdbPortTable) +{ + std::deque entries; + + appdbPortTable.pops(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) == "response";} + ); + if (cit != fieldValues.cend()) { + const std::string f = cit->first; + const std::string v = cit->second; + + MUXLOGINFO(boost::format("port: %s, operation: %s, f: %s, v: %s") % + port % + oprtation % + f % + v + ); +// swss::Table table(mAppDbPtr.get(), APP_MUX_CABLE_RESPONSE_TABLE_NAME); +// table.hdel(port, "response"); + mMuxManagerPtr->processProbeMuxState(port, v); + } + } +} + +// +// ---> handleMuxStateNotifiction(swss::SubscriberStateTable &statedbPortTable); +// +// statedbPortTable (in) reference to state db port table +// +void DbInterface::handleMuxStateNotifiction(swss::SubscriberStateTable &statedbPortTable) +{ + std::deque entries; + + statedbPortTable.pops(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; + + MUXLOGINFO(boost::format("port: %s, operation: %s, f: %s, v: %s") % + port % + oprtation % + f % + v + ); + mMuxManagerPtr->addOrUpdateMuxPortMuxState(port, v); + } + } +} + +// +// ---> handleSwssNotification(); +// +// main thread method for handling SWSS notification +// +void DbInterface::handleSwssNotification() +{ + std::shared_ptr configDbPtr = std::make_shared ("CONFIG_DB", 0); + std::shared_ptr appDbPtr = std::make_shared ("APPL_DB", 0); + std::shared_ptr stateDbPtr = std::make_shared ("STATE_DB", 0); + + // For reading Link Prober configurations from the localhost table name + swss::SubscriberStateTable configDbLocalhostTable(configDbPtr.get(), "LOCALHOST"/*CFG_LOCALHOST_TABLE_NAME*/); + + swss::SubscriberStateTable configDbMuxTable(configDbPtr.get(), CFG_MUX_CABLE_TABLE_NAME); + + // for link up/down, should be in state db down the road + swss::SubscriberStateTable appDbPortTable(appDbPtr.get(), APP_PORT_TABLE_NAME); + // for command responses from the driver + swss::SubscriberStateTable appDbMuxResponseTable(appDbPtr.get(), APP_MUX_CABLE_RESPONSE_TABLE_NAME); + // for getting state db MUX state when orchagent updates it + swss::SubscriberStateTable stateDbPortTable(stateDbPtr.get(), STATE_MUX_CABLE_TABLE_NAME); + + getLoopback2InterfaceInfo(configDbPtr, stateDbPtr); + getServerIpAddress(configDbPtr); + + NetMsgInterface netMsgInterface(*this); + swss::NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &netMsgInterface); + swss::NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &netMsgInterface); + + swss::NetLink netlinkNeighbor; + netlinkNeighbor.registerGroup(RTNLGRP_NEIGH); + netlinkNeighbor.dumpRequest(RTM_GETNEIGH); + + swss::Select swssSelect; + swssSelect.addSelectable(&configDbLocalhostTable); + swssSelect.addSelectable(&configDbMuxTable); + swssSelect.addSelectable(&appDbPortTable); + swssSelect.addSelectable(&appDbMuxResponseTable); + swssSelect.addSelectable(&stateDbPortTable); + swssSelect.addSelectable(&netlinkNeighbor); + + while (mPollSwssNotifcation) { + swss::Selectable *selectable; + int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC); + + if (ret == swss::Select::ERROR) { + MUXLOGERROR("Error had been returned in select"); + continue; + } + else if (ret == swss::Select::TIMEOUT) { + continue; + } + else if (ret != swss::Select::OBJECT) { + MUXLOGERROR(boost::format("Unknown return value from Select: %d") % ret); + continue; + } + + if (selectable == static_cast (&configDbLocalhostTable)) { + handleLocalhostConfigNotifiction(configDbLocalhostTable); + } + else if (selectable == static_cast (&configDbMuxTable)) { + handleMuxPortConfigNotifiction(configDbMuxTable); + } + else if (selectable == static_cast (&appDbPortTable)) { + handleLinkStateNotifiction(appDbPortTable); + } + else if (selectable == static_cast (&appDbMuxResponseTable)) { + handleMuxResponseNotifiction(appDbMuxResponseTable); + } + else if (selectable == static_cast (&stateDbPortTable)) { + handleMuxStateNotifiction(stateDbPortTable); + } + else if (selectable == static_cast (&netlinkNeighbor)) { + continue; + } + else { + MUXLOGERROR("Unknown object returned by select"); + } + } + + mMuxManagerPtr->terminate(); +} + +} /* namespace common */ diff --git a/src/linkmgrd/src/DbInterface.h b/src/linkmgrd/src/DbInterface.h new file mode 100644 index 000000000000..05b128fc11e3 --- /dev/null +++ b/src/linkmgrd/src/DbInterface.h @@ -0,0 +1,298 @@ +/* + * DbInterface.h + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#ifndef DBINTERFACE_H_ +#define DBINTERFACE_H_ + +#include +#include +#include + +#include "swss/dbconnector.h" +#include "swss/producerstatetable.h" +#include "swss/subscriberstatetable.h" + +#include "mux_state/MuxState.h" + +namespace mux { +class MuxManager; +} + +namespace mux +{ +using ServerIpPortMap = std::map; + +/** + *@class DbInterface + * + *@brief DbInterface interfaces with Redis DB, reads MUX config, and + * listens to updates posted to the subscriber tables. + */ +class DbInterface +{ +public: + /** + *@method DbInterface + * + *@brief class default constructor + */ + DbInterface() = delete; + + /** + *@method DbInterface + * + *@brief class copy constructor + * + *@param DbInterface (in) reference to DbInterface object to be copied + */ + DbInterface(const DbInterface &) = delete; + + /** + *@method DbInterface + * + *@brief class constructor + * + *@param muxManager (in) pointer to MuxManager object + *@param ioService (in) pointer to Boost IO Service + */ + DbInterface(mux::MuxManager *muxManager, boost::asio::io_service *ioService); + + /** + *@method ~DbInterface + * + *@brief class destructor + */ + virtual ~DbInterface() = default; + + /** + *@method getStrand + * + *@brief getter for Boost strand object + * + *@return reference to Boost strand object + */ + inline boost::asio::io_service::strand& getStrand() {return mStrand;}; + + /** + *@method getMuxState + * + *@brief retrieve the current MUX state + * + *@param portName (in) MUX/port name + * + *@return none + */ + virtual void getMuxState(const std::string &portName); + + /** + *@method setMuxState + * + *@brief set 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 setMuxState(const std::string &portName, mux_state::MuxState::Label label); + + /** + *@method probeMuxState + * + *@brief trigger xcvrd to read MUX state using i2c + * + *@param portName (in) MUX/port name + * + *@return label of MUX state + */ + virtual void probeMuxState(const std::string &portName); + + /** + *@method initialize + * + *@brief initialize DB and start SWSS listening thread + * + *@return none + */ + void initialize(); + + /** + *@method updateServerMacAddress + * + *@brief Update Server MAC address behind a MUX port + * + *@param serverIp (in) Server IP address + *@param serverMac (in) Server MAC address + * + *@return none + */ + void updateServerMacAddress(boost::asio::ip::address serverIp, const uint8_t *serverMac); + + /** + *@method stopSwssNotificationPoll + * + *@brief stop SWSS listening thread + * + *@return none + */ + void stopSwssNotificationPoll() {mPollSwssNotifcation = false;}; + +private: + /** + *@method handleGetMuxState + * + *@brief get state db MUX state + * + *@param portName (in) MUX/port name + * + *@return none + */ + void handleGetMuxState(const std::string portName); + + /** + *@method handleSetMuxState + * + *@brief set MUX state in APP DB for orchagent processing + * + *@param portName (in) MUX/port name + *@param label (in) label of target state + * + *@return none + */ + void handleSetMuxState(const std::string portName, mux_state::MuxState::Label label); + + /** + *@method handleProbeMuxState + * + *@brief trigger xcvrd to read MUX state using i2c + * + *@param portName (in) MUX/port name + * + *@return label of MUX state + */ + void handleProbeMuxState(const std::string portName); + + /** + *@method getLoopback2InterfaceInfo + * + *@brief retrieve Loopback2 interface information and block until it shows as OK in the state db + * + *@param configDbConnector config db connector + *@param configDbConnector state db connector + * + *@return none + */ + void getLoopback2InterfaceInfo( + std::shared_ptr configDbConnector, + std::shared_ptr stateDbConnector + ); + + /** + *@method getServerIpAddress + * + *@brief retrieve server/blades IP address and builds a map of IP to port name + * + *@param configDbConnector config db connector + * + *@return none + */ + void getServerIpAddress(std::shared_ptr configDbConnector); + + /** + *@method handleMuxPortConfigNotifiction + * + *@brief handles MUX port configuration change notification + * + *@param configMuxTable (in) reference to MUX config table + * + *@return none + */ + void handleMuxPortConfigNotifiction(swss::SubscriberStateTable &configMuxTable); + + /** + *@method handleLocalhostConfigNotifiction + * + *@brief handles localhost configuration change notification + * + *@param configLocalhostTable (in) reference to localhost config table + * + *@return none + */ + void handleLocalhostConfigNotifiction(swss::SubscriberStateTable &configLocalhostTable); + + /** + *@method handleLinkStateNotifiction + * + *@brief handles link state change notification + * + *@param appdbPortTable (in) reference to app db port table + * + *@return none + */ + void handleLinkStateNotifiction(swss::SubscriberStateTable &appdbPortTable); + + /** + *@method handleMuxResponseNotifiction + * + *@brief handles MUX response (from xcvrd) notification + * + *@param appdbPortTable (in) reference to app db port table + * + *@return none + */ + void handleMuxResponseNotifiction(swss::SubscriberStateTable &appdbPortTable); + + /** + *@method handleMuxStateNotifiction + * + *@brief handles MUX state (from orchagent) notification + * + *@param statedbPortTable (in) reference to state db port table + * + *@return none + */ + void handleMuxStateNotifiction(swss::SubscriberStateTable &statedbPortTable); + + /** + *@method handleSwssNotification + * + *@brief main thread method for handling SWSS notification + * + *@return none + */ + void handleSwssNotification(); + +private: + static std::vector mMuxState; + +private: + mux::MuxManager *mMuxManagerPtr; + bool mPollSwssNotifcation = true; + + std::shared_ptr mAppDbPtr; + std::shared_ptr mStateDbPtr; + std::shared_ptr mMuxStateTablePtr; + + // for communicating with orchagent + std::shared_ptr mAppDbMuxTablePtr; + // for communicating with the driver (probing the mux) + std::shared_ptr mAppDbMuxCommandTablePtr; + // for writing the current mux health, should it be health table? + std::shared_ptr mStateDbMuxTablePtr; + + std::shared_ptr mMuxStateDbThreadPtr; + std::shared_ptr mConfigDbThreadPtr; + std::shared_ptr mSwssThreadPtr; + + boost::asio::io_service::strand mStrand; + + ServerIpPortMap mServerIpPortMap; +}; + +} /* namespace common */ + +#endif /* DBINTERFACE_H_ */ diff --git a/src/linkmgrd/src/LinkMgrdMain.cpp b/src/linkmgrd/src/LinkMgrdMain.cpp new file mode 100644 index 000000000000..b851b865cb58 --- /dev/null +++ b/src/linkmgrd/src/LinkMgrdMain.cpp @@ -0,0 +1,111 @@ +//============================================================================ +// Name : linkMgrdMain.cpp +// Author : Tamer Ahmed +// Version : +// Copyright : Your copyright notice +// Description : Main program for dual ToR (Gemini Project) +//============================================================================ + +#include + +#include +#include + +#include "MuxManager.h" +#include "MuxPort.h" +#include "common/MuxConfig.h" +#include "common/MuxLogger.h" +#include "link_manager/LinkManagerStateMachine.h" +#include "link_prober/LinkProberStateMachine.h" +#include "link_prober/LinkProber.h" +#include "link_prober/IcmpPayload.h" + +// Private namespace for this module +namespace { + // Some namespace and type aliases... + namespace program_options = boost::program_options; + + static auto DEFAULT_CONFIG_FILE = "linkmgrd-config.json"; + static auto DEFAULT_LOGGING_FILTER_LEVEL = boost::log::trivial::debug; + + void InitializeLogger(std::string execName, boost::log::trivial::severity_level level) + { + std::string progName = execName.substr(execName.find_last_of('/') + 1); + std::string logFile = "/var/log/mux/" + progName + ".log"; + + common::MuxLogger::getInstance()->initialize(progName, logFile, level); + } + +} // end namespace + + +// +// Main program entry point for Gemini MUX Manager (aka linkmgrd). +// +int main(int argc, const char* argv[]) +{ + int retValue = EXIT_SUCCESS; + + // + // Constants for command line argument strings: + // + boost::log::trivial::severity_level level; + std::string configFile; + + program_options::options_description description("linkmgrd options"); + description.add_options() + ("help,h", + "Print usage information.") + ("linkmgrd-config,l", + program_options::value(&configFile)->value_name("")-> + default_value(DEFAULT_CONFIG_FILE), + "Link Manager configuration file.") + ("verbosity,v", + program_options::value(&level)->value_name("")-> + default_value(DEFAULT_LOGGING_FILTER_LEVEL), + "Logging verbosity level.") + ; + + // + // Actually parse options, print verbose usage when it fails + // + program_options::variables_map variableMap; + try { + store(parse_command_line(argc, argv, description), variableMap); + program_options::notify(variableMap); + } + catch (program_options::error_with_option_name& e) { + std::cerr << "Command Line Syntax Error: " << e.what() << std::endl; + std::cout << description << "\n"; + retValue = EXIT_FAILURE; + } + catch (program_options::error& e) { + std::cerr << "Command Line Error: " << e.what() << std::endl; + std::cout << description << "\n"; + retValue = EXIT_FAILURE; + } + + if (retValue == EXIT_SUCCESS && variableMap.count("help")) { + std::cout << description << "\n"; + + retValue = EXIT_FAILURE; + } + + if (retValue == EXIT_SUCCESS) { + InitializeLogger(argv[0], level); + std::stringstream ss; + ss << "configFile: " << configFile + << ", level: " << level; + MUXLOGINFO(ss.str()); + + // initialize static data + link_prober::IcmpPayload::generateGuid(); + link_manager::LinkManagerStateMachine::initializeTransitionFunctionTable(); + + std::shared_ptr muxManagerPtr = std::make_shared (configFile); + muxManagerPtr->initialize(); + muxManagerPtr->run(); + } + + return retValue; +} diff --git a/src/linkmgrd/src/MuxManager.cpp b/src/linkmgrd/src/MuxManager.cpp new file mode 100644 index 000000000000..0a327accf5dd --- /dev/null +++ b/src/linkmgrd/src/MuxManager.cpp @@ -0,0 +1,264 @@ +/* + * MuxManager.cpp + * + * Created on: Oct 4, 2020 + * Author: tamer + */ +#include +#include +#include +#include + +#include + +#include "common/MuxException.h" +#include "common/MuxLogger.h" +#include "MuxManager.h" + +namespace mux +{ +// +// ---> MuxManager(std::string &configFilename); +// +// class constructor +// +MuxManager::MuxManager(std::string &configFilename) : + mConfigFilename(configFilename), + mMuxConfig(), + mWork(mIoService), + mSignalSet(boost::asio::signal_set(mIoService, SIGINT, SIGTERM)), + mDbInterface(this, &mIoService) +{ + mSignalSet.add(SIGUSR1); + mSignalSet.add(SIGUSR2); + mSignalSet.async_wait(boost::bind(&MuxManager::handleSignal, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::signal_number + )); +} + +// +// ---> initialize(); +// +// initialize MuxManager class and creates DbInterface instance that reads/listen from/to Redis db +// +void MuxManager::initialize() +{ + for (uint8_t i = 0; (mMuxConfig.getNumberOfThreads() > 2) && + (i < mMuxConfig.getNumberOfThreads() - 2); i++) { + mThreadGroup.create_thread( + boost::bind(&boost::asio::io_service::run, &mIoService) + ); + } + + mDbInterface.initialize(); +} + +// +// ---> run(); +// +// start Boost IO Service event loop +// +void MuxManager::run() +{ + mIoService.run(); +} + +// +// ---> terminate(); +// +// stop and terminate Boost IO Service event loop +// +void MuxManager::terminate() +{ + mIoService.stop(); + mIoService.reset(); + mThreadGroup.join_all(); +} + +// +// ---> addOrUpdateMuxPort(const std::string &portName, boost::asio::ip::address); +// +// update MUX port server/blade IPv4 Address. If port is not found, create new MuxPort object +// +void MuxManager::addOrUpdateMuxPort(const std::string &portName, boost::asio::ip::address smartNicIpAddress) +{ + MUXLOGINFO(boost::format("%s: server IP: %s") % portName % smartNicIpAddress); + boost::system::error_code errorCode; + std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + + if (smartNicIpAddress.is_v4()) { + muxPortPtr->setServerIpv4Address(smartNicIpAddress); + muxPortPtr->initializeLinkProber(); + } + else if (smartNicIpAddress.is_v6()) { + // handle IPv6 probing + } +} + +// +// ---> updateMuxPortConfig(const std::string &portName, const std::string &config); +// +// update MUX port server/blade IPv4 Address. If port is not found, create new MuxPort object +// +void MuxManager::updateMuxPortConfig(const std::string &portName, const std::string &config) +{ + MUXLOGINFO(boost::format("%s: Mux port config: %s") % portName % config); + + PortMapIterator portMapIterator = mPortMap.find(portName); + if (portMapIterator != mPortMap.end()) { + portMapIterator->second->handleMuxConfig(config); + } +} + +// +// ---> addOrUpdateMuxPortLinkState(const std::string &portName, const std::string &linkState); +// +// update MUX port server/blade IPv4 Address. If port is not found, create new MuxPort object +// +void MuxManager::addOrUpdateMuxPortLinkState(const std::string &portName, const std::string &linkState) +{ + MUXLOGINFO(boost::format("%s: link state: %s") % portName % linkState); + + std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + muxPortPtr->handleLinkState(linkState); +} + +// +// ---> addOrUpdateMuxPortMuxState(const std::string &portName, const std::string &muxState); +// +// update MUX port state db notification +// +void MuxManager::addOrUpdateMuxPortMuxState(const std::string &portName, const std::string &muxState) +{ + MUXLOGINFO(boost::format("%s: state db mux state: %s") % portName % muxState); + + std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + muxPortPtr->handleMuxState(muxState); +} + +// +// ---> processGetServerMacAddress(const std::string &portName, const std::array &address); +// +// update MUX port server MAC address +// +void MuxManager::processGetServerMacAddress( + const std::string &portName, + const std::array &address +) +{ + MUXLOGINFO(portName); + + PortMapIterator portMapIterator = mPortMap.find(portName); + if (portMapIterator != mPortMap.end()) { + portMapIterator->second->handleGetServerMacAddress(address); + } +} + +// +// ---> processGetMuxState(const std::string &portName, const std::string &muxState); +// +// update MUX port app db notification +// +void MuxManager::processGetMuxState(const std::string &portName, const std::string &muxState) +{ + MUXLOGINFO(boost::format("%s: app db mux state: %s") % portName % muxState); + + PortMapIterator portMapIterator = mPortMap.find(portName); + if (portMapIterator != mPortMap.end()) { + portMapIterator->second->handleGetMuxState(muxState); + } +} + +// +// ---> processProbeMuxState(const std::string &portName, const std::string &muxState); +// +// update MUX port app db notification +// +void MuxManager::processProbeMuxState(const std::string &portName, const std::string &muxState) +{ + MUXLOGINFO(boost::format("%s: app db mux state: %s") % portName % muxState); + + PortMapIterator portMapIterator = mPortMap.find(portName); + if (portMapIterator != mPortMap.end()) { + portMapIterator->second->handleProbeMuxState(muxState); + } +} + +// +// ---> getMuxPortPtrOrThrow(const std::string &portName); +// +// retrieve a pointer to MuxPort if it exist or create a new MuxPort object +// +std::shared_ptr MuxManager::getMuxPortPtrOrThrow(const std::string &portName) +{ + std::shared_ptr muxPortPtr; + + try { + PortMapIterator portMapIterator = mPortMap.find(portName); + if (portMapIterator == mPortMap.end()) { + uint16_t serverId = atoi(portName.substr(portName.find_last_not_of("0123456789") + 1).c_str()); + muxPortPtr = std::make_shared ( + &mDbInterface, + mMuxConfig, + portName, + serverId, + mIoService + ); + mPortMap.insert({portName, muxPortPtr}); + } + else { + muxPortPtr = portMapIterator->second; + } + } + catch (const std::bad_alloc& ex) { + std::ostringstream errMsg; + errMsg << "Failed allocate memory. Exception details: " << ex.what(); + + throw MUX_ERROR(BadAlloc, errMsg.str()); + } + + return muxPortPtr; +} + +// +// ---> handleSignal(const boost::system::error_code errorCode, int signalNumber)' +// +// handles system signal +// +void MuxManager::handleSignal(const boost::system::error_code errorCode, int signalNumber) +{ + if (!errorCode) { + MUXLOGFATAL(boost::format("Got signal: %d") % signalNumber); + + boost::log::trivial::severity_level level; + switch (signalNumber) { + case SIGINT: + case SIGTERM: + mDbInterface.stopSwssNotificationPoll(); + mIoService.stop(); + break; + case SIGUSR1: + level = common::MuxLogger::getInstance()->getLevel() == boost::log::trivial::severity_level::trace ? + boost::log::trivial::severity_level::fatal : + static_cast (common::MuxLogger::getInstance()->getLevel() - 1); + common::MuxLogger::getInstance()->setLevel(level); + MUXLOGFATAL(boost::format("Updated log level to: %s") % level); + break; + case SIGUSR2: + //@TODO: try to dumb state transition of all MUXes here + break; + default: + break; + } + } + + mSignalSet.async_wait(boost::bind(&MuxManager::handleSignal, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::signal_number + )); +} + +} /* namespace mux */ diff --git a/src/linkmgrd/src/MuxManager.h b/src/linkmgrd/src/MuxManager.h new file mode 100644 index 000000000000..3e1bbef78df7 --- /dev/null +++ b/src/linkmgrd/src/MuxManager.h @@ -0,0 +1,291 @@ +/* + * MuxManager.h + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#ifndef MUXMANAGER_H_ +#define MUXMANAGER_H_ + +#include +#include + +#include +#include +#include + +#include "MuxPort.h" +#include "common/MuxConfig.h" +#include "DbInterface.h" + +namespace mux +{ +using PortMap = std::map>; +using PortMapIterator = PortMap::iterator; + +/** + *@class MuxManager + * + *@brief host collection MuxPort object, each has MuxPort configuration, LinkManagerStateMachine. + */ +class MuxManager +{ +public: + /** + *@method MuxManager + * + *@brief class default constructor + */ + MuxManager() = delete; + + /** + *@method MuxManager + * + *@brief class copy constructor + * + *@param MuxManager (in) reference to MuxManager object to be copied + */ + MuxManager(const MuxManager &) = delete; + + /** + *@method MuxManager + * + *@brief class constructor + * + *@param configFilename (in) json file contain linkmgrd config (deprecated) + */ + MuxManager(std::string &configFilename); + + /** + *@method ~MuxManager + * + *@brief class destructor + */ + virtual ~MuxManager() = default; + + /** + *@method getIoService + * + *@brief getter for Boost IO Service/Context object + * + *@return reference to Boost IO Service/Context object + */ + inline boost::asio::io_service& getIoService() {return mIoService;}; + + /** + *@method getDbInterface + * + *@brief getter for DbInterface object + * + *@return reference to DbInterface object + */ + inline mux::DbInterface& getDbInterface() {return mDbInterface;}; + + /** + *@method setTimeoutIpv4_msec + * + *@brief setter for IPv4 LinkProber timeout in msec + * + *@param timeout_msec (in) timeout in msec + * + *@return none + */ + inline void setTimeoutIpv4_msec(uint32_t timeout_msec) {mMuxConfig.setTimeoutIpv4_msec(timeout_msec);}; + + /** + *@method setTimeoutIpv6_msec + * + *@brief setter for IPv6 LinkProber timeout in msec + * + *@param timeout_msec (in) timeout in msec + * + *@return none + */ + inline void setTimeoutIpv6_msec(uint32_t timeout_msec) {mMuxConfig.setTimeoutIpv6_msec(timeout_msec);}; + + /** + *@method setStateChangeRetryCount + * + *@brief setter for LinkProber state change retry count + * + *@param stateChangeRetryCount (in) state change retry count + * + *@return none + */ + inline void setStateChangeRetryCount(uint32_t stateChangeRetryCount) {mMuxConfig.setStateChangeRetryCount(stateChangeRetryCount);}; + + /** + *@method setSuspendTimeout_msec + * + *@brief setter for LinkProber suspend timer timeout + * + *@param suspendTimeout_msec (in) suspend timer timeout + * + *@return none + */ + inline void setSuspendTimeout_msec(uint32_t suspendTimeout_msec) {mMuxConfig.setSuspendTimeout_msec(suspendTimeout_msec);}; + + /** + *@method setSuspendTimeout_msec + * + *@brief setter for LinkProber suspend timer timeout + * + *@param suspendTimeout_msec (in) suspend timer timeout + * + *@return none + */ + inline void setLoopbackIpv4Address(boost::asio::ip::address& address) {mMuxConfig.setLoopbackIpv4Address(address);}; + + /** + *@method initialize + * + *@brief initialize MuxManager class and creates DbInterface instance that reads/listen from/to Redis db + * + *@return none + */ + void initialize(); + + /** + *@method run + * + *@brief start Boost IO Service event loop + * + *@return none + */ + void run(); + + /** + *@method terminate + * + *@brief stop and terminate Boost IO Service event loop + * + *@return none + */ + void terminate(); + + /** + *@method addOrUpdateMuxPort + * + *@brief update MUX port server/blade IPv4 Address. If port is not found, create new MuxPort object + * + *@param portName (in) Mux port name + *@param smartNicIpAddress (in) server/blade IP address + * + *@return none + */ + void addOrUpdateMuxPort(const std::string &portName, boost::asio::ip::address smartNicIpAddress); + + /** + *@method updateMuxPortConfig + * + *@brief update MUX port server/blade IPv4 Address. If port is not found, create new MuxPort object + * + *@param portName (in) Mux port name + *@param linkState (in) Mux port link state + * + *@return none + */ + void updateMuxPortConfig(const std::string &portName, const std::string &linkState); + + /** + *@method addOrUpdateMuxPortLinkState + * + *@brief update MUX port server/blade IPv4 Address. If port is not found, create new MuxPort object + * + *@param portName (in) Mux port name + *@param linkState (in) Mux port link state + * + *@return none + */ + void addOrUpdateMuxPortLinkState(const std::string &portName, const std::string &linkState); + + /** + *@method addOrUpdateMuxPortMuxState + * + *@brief update MUX port state db notification + * + *@param portName (in) Mux port name + *@param muxState (in) Mux port state + * + *@return none + */ + void addOrUpdateMuxPortMuxState(const std::string &portName, const std::string &muxState); + + /** + *@method processGetServerMacAddress + * + *@brief update MUX port server MAC address + * + *@param portName (in) Mux port name + *@param address (in) Server MAC address + * + *@return none + */ + void processGetServerMacAddress(const std::string &portName, const std::array &address); + + /** + *@method processGetMuxState + * + *@brief update MUX port app db notification + * + *@param portName (in) Mux port name + *@param muxState (in) Mux port state + * + *@return none + */ + void processGetMuxState(const std::string &portName, const std::string &muxState); + + /** + *@method processProbeMuxState + * + *@brief update MUX port app db notification + * + *@param portName (in) Mux port name + *@param muxState (in) Mux port state + * + *@return none + */ + void processProbeMuxState(const std::string &portName, const std::string &muxState); + +private: + /** + *@method getMuxPortPtrOrThrow + * + *@brief retrieve a pointer to MuxPort if it exist or create a new MuxPort object + * + *@param portName (in) Mux port name + * + *@return pointer to MuxPort object + */ + std::shared_ptr getMuxPortPtrOrThrow(const std::string &portName); + + /** + *@method handleSignal + * + *@brief handles system signal + * + *@param errorCode (in) Boost error code + *@param signalNumber (in) Signal number + * + *@return none + */ + void handleSignal(const boost::system::error_code errorCode, int signalNumber); + +private: + std::string mConfigFilename; + common::MuxConfig mMuxConfig; + + boost::asio::io_service mIoService; + boost::asio::io_service::work mWork; + boost::thread_group mThreadGroup; + boost::asio::signal_set mSignalSet; + + mux::DbInterface mDbInterface; + + PortMap mPortMap; +}; + +} /* namespace mux */ + +#endif /* MUXMANAGER_H_ */ diff --git a/src/linkmgrd/src/MuxPort.cpp b/src/linkmgrd/src/MuxPort.cpp new file mode 100644 index 000000000000..7a1ea64ae0b9 --- /dev/null +++ b/src/linkmgrd/src/MuxPort.cpp @@ -0,0 +1,199 @@ +/* + * MuxPort.cpp + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "MuxPort.h" +#include "common/MuxLogger.h" + +namespace mux +{ +// +// ---> MuxPort( +// mux::DbInterface *dbInterface, +// common::MuxConfig &muxConfig, +// const std::string &portName, +// uint16_t serverId, +// boost::asio::io_service &ioService +// ); +// +// class constructor +// +MuxPort::MuxPort( + mux::DbInterface *dbInterface, + common::MuxConfig &muxConfig, + const std::string &portName, + uint16_t serverId, + boost::asio::io_service &ioService +) : + mDbInterface(dbInterface), + mMuxPortConfig( + muxConfig, + portName, + serverId + ), + mStrand(ioService), + mLinkManagerStateMachine( + this, + mStrand, + mMuxPortConfig + ) +{ + assert(dbInterface != nullptr); +} + +// +// ---> handleLinkState(const std::string &linkState); +// +// handles link state updates +// +void MuxPort::handleLinkState(const std::string &linkState) +{ + MUXLOGDEBUG(boost::format("port: %s, state db link state: %s") % mMuxPortConfig.getPortName() % linkState); + + link_state::LinkState::Label label = link_state::LinkState::Label::Down; + if (linkState == "up") { + label = link_state::LinkState::Label::Up; + } + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachine::handleSwssLinkStateNotification, + &mLinkManagerStateMachine, + label + ))); +} + +// +// ---> handleGetServerMacAddress(const std::array &address) +// +// handles get Server MAC address +// +void MuxPort::handleGetServerMacAddress(const std::array &address) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachine::handleGetServerMacAddressNotification, + &mLinkManagerStateMachine, + address + ))); +} + +// +// ---> handleGetMuxState(const std::string &muxState); +// +// handles MUX state updates +// +void MuxPort::handleGetMuxState(const std::string &muxState) +{ + MUXLOGDEBUG(boost::format("port: %s, state db mux state: %s") % mMuxPortConfig.getPortName() % muxState); + + mux_state::MuxState::Label label = mux_state::MuxState::Label::Unknown; + if (muxState == "active") { + label = mux_state::MuxState::Label::Active; + } else if (muxState == "standby") { + label = mux_state::MuxState::Label::Standby; + } + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachine::handleGetMuxStateNotification, + &mLinkManagerStateMachine, + label + ))); +} + +// +// ---> handleProbeMuxState(const std::string &muxState); +// +// handles MUX state updates +// +void MuxPort::handleProbeMuxState(const std::string &muxState) +{ + MUXLOGDEBUG(boost::format("port: %s, state db mux state: %s") % mMuxPortConfig.getPortName() % muxState); + + mux_state::MuxState::Label label; + if (muxState == "active") { + label = mux_state::MuxState::Label::Active; + } else if (muxState == "standby") { + label = mux_state::MuxState::Label::Standby; + } else if (muxState == "unknown") { + label = mux_state::MuxState::Label::Unknown; + } + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachine::handleProbeMuxStateNotification, + &mLinkManagerStateMachine, + label + ))); +} + +// +// ---> handleMuxState(const std::string &muxState); +// +// handles MUX state updates +// +void MuxPort::handleMuxState(const std::string &muxState) +{ + MUXLOGDEBUG(boost::format("port: %s, state db mux state: %s") % mMuxPortConfig.getPortName() % muxState); + + mux_state::MuxState::Label label = mux_state::MuxState::Label::Unknown; + if (muxState == "active") { + label = mux_state::MuxState::Label::Active; + } + else if (muxState == "standby") { + label = mux_state::MuxState::Label::Standby; + } + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachine::handleMuxStateNotification, + &mLinkManagerStateMachine, + label + ))); +} + +// +// ---> handleMuxConfig(const std::string &config); +// +// handles MUX config updates when switching between auto/active +// +void MuxPort::handleMuxConfig(const std::string &config) +{ + MUXLOGDEBUG(boost::format("port: %s, config db mux config: %s") % mMuxPortConfig.getPortName() % config); + + common::MuxPortConfig::Mode mode = common::MuxPortConfig::Auto; + if (config == "active") { + mode = common::MuxPortConfig::Active; + } + + mMuxPortConfig.setMode(mode); + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachine::handleMuxConfigNotification, + &mLinkManagerStateMachine, + mode + ))); +} + +} /* namespace mux */ diff --git a/src/linkmgrd/src/MuxPort.h b/src/linkmgrd/src/MuxPort.h new file mode 100644 index 000000000000..2b8ed2d7a0c2 --- /dev/null +++ b/src/linkmgrd/src/MuxPort.h @@ -0,0 +1,246 @@ +/* + * MuxPort.h + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#ifndef MUXPORT_H_ +#define MUXPORT_H_ + +#include + +#include +#include +#include + +#include + +#include "link_prober/LinkProber.h" +#include "link_prober/LinkProberStateMachine.h" +#include "link_manager/LinkManagerStateMachine.h" + +#include "common/MuxPortConfig.h" +#include "DbInterface.h" + +namespace mux +{ + +/** + *@class MuxPort + * + *@brief Hold MUX configuration data, state machines and link prober + */ +class MuxPort: public std::enable_shared_from_this +{ +public: + /** + *@method MuxPort + * + *@brief class default constructor + */ + MuxPort() = delete; + + /** + *@method MuxPort + * + *@brief class copy constructor + * + *@param MuxPort (in) reference to MuxPort object to be copied + */ + MuxPort(const MuxPort &) = delete; + + /** + *@method MuxPort + * + *@brief class constructor + * + *@param dbInterface (in) pointer to DbInterface object + *@param muxConfig (in) reference to MuxConfig object + *@param portName (in) reference to port name + *@param serverId (in) server/blade id + *@param ioService (in) reference to Boost IO Service object + */ + MuxPort( + mux::DbInterface *dbInterface, + common::MuxConfig &muxConfig, + const std::string &portName, + uint16_t serverId, + boost::asio::io_service &ioService + ); + + /** + *@method ~MuxPort + * + *@brief class destructor + */ + virtual ~MuxPort() = default; + + /** + *@method getMuxPortConfig + * + *@brief getter for MuxPortConfig object + * + *@return reference to MuxPortConfig object + */ + inline const common::MuxPortConfig& getMuxPortConfig() const {return mMuxPortConfig;}; + + /** + *@method setMuxState + * + *@brief set MUX state in APP DB for orchagent processing + * + *@param portName (in) MUX/port name + *@param label (in) label of target state + * + *@return none + */ + inline void setMuxState(mux_state::MuxState::Label label) {mDbInterface->setMuxState(mMuxPortConfig.getPortName(), label);}; + + /** + *@method getMuxState + * + *@brief retrieve the current MUX state + * + *@param portName (in) MUX/port name + * + *@return none + */ + inline void getMuxState() {mDbInterface->getMuxState(mMuxPortConfig.getPortName());}; + + /** + *@method probeMuxState + * + *@brief trigger xcvrd to read MUX state using i2c + * + *@param portName (in) MUX/port name + * + *@return label of MUX state + */ + inline void probeMuxState() {mDbInterface->probeMuxState(mMuxPortConfig.getPortName());}; + + /** + *@method setServerIpv4Address + * + *@brief setter for server/blade IPv4 address + * + *@param address (in) server IPv4 address + * + *@return none + */ + inline void setServerIpv4Address(const boost::asio::ip::address &address) {mMuxPortConfig.setBladeIpv4Address(address);}; + + /** + *@method initializeLinkProber + * + *@brief initialize link prober after reading the server/blade IP address + * + *@return none + */ + inline void initializeLinkProber() {mLinkManagerStateMachine.initializeLinkProber();}; + + /** + *@method handleLinkState + * + *@brief handles link state updates + * + *@param linkState (in) link state + * + *@return none + */ + void handleLinkState(const std::string &linkState); + + /** + *@method handleGetServerMacAddress + * + *@brief handles get Server MAC address + * + *@param address (in) Server MAC address + * + *@return none + */ + void handleGetServerMacAddress(const std::array &address); + + /** + *@method handleGetMuxState + * + *@brief handles get MUX state updates + * + *@param muxState (in) link state + * + *@return none + */ + void handleGetMuxState(const std::string &muxState); + + /** + *@method handleProbeMuxState + * + *@brief handles probe MUX state updates + * + *@param muxState (in) link state + * + *@return none + */ + void handleProbeMuxState(const std::string &muxState); + + /** + *@method handleMuxState + * + *@brief handles MUX state updates + * + *@param muxState (in) link state + * + *@return none + */ + void handleMuxState(const std::string &muxState); + + /** + *@method handleMuxConfig + * + *@brief handles MUX config updates when switching between auto/active + * + *@param config (in) MUX new config; auto/active + * + *@return none + */ + void handleMuxConfig(const std::string &config); + +protected: + /** + *@method getLinkManagerStateMachine + * + *@brief getter for LinkManagerStateMachine object (used during unit test) + * + *@return pointer to LinkManagerStateMachine object + */ + link_manager::LinkManagerStateMachine* getLinkManagerStateMachine() {return &mLinkManagerStateMachine;}; + + /** + *@method setLinkProberPtr + * + *@brief setter for LinkProber object (used during unit test) + * + *@param linkProberPtr (in) new link prober + */ + void setLinkProberPtr(std::shared_ptr linkProberPtr) {mLinkManagerStateMachine.setLinkProberPtr(linkProberPtr);}; + + /** + *@method setComponentInitState + * + *@brief setter for state machine component initial state (used during unit test) + * + *@param component (in) component index + */ + void setComponentInitState(uint8_t component) {mLinkManagerStateMachine.setComponentInitState(component);}; + +private: + mux::DbInterface *mDbInterface = nullptr; + common::MuxPortConfig mMuxPortConfig; + boost::asio::io_service::strand mStrand; + + link_manager::LinkManagerStateMachine mLinkManagerStateMachine; +}; + +} /* namespace mux */ + +#endif /* MUXPORT_H_ */ diff --git a/src/linkmgrd/src/NetMsgInterface.cpp b/src/linkmgrd/src/NetMsgInterface.cpp new file mode 100644 index 000000000000..552e15fc52ad --- /dev/null +++ b/src/linkmgrd/src/NetMsgInterface.cpp @@ -0,0 +1,76 @@ +/* + * NetMsgInterface.cpp + * + * Created on: Jan 12, 2021 + * Author: taahme + */ + +#include +#include +#include +#include +#include + +#include "swss/linkcache.h" +#include "swss/macaddress.h" + +#include "NetMsgInterface.h" +#include "common/MuxLogger.h" +#include "common/MuxException.h" + +#define MAX_ADDR_SIZE 64 + +namespace mux +{ + +// +// ---> NetMsgInterface(DbInterface &dbInterface); +// +// class constructor +// +NetMsgInterface::NetMsgInterface(DbInterface &dbInterface) : + mDbInterface(dbInterface) +{ +} + +// +// ---> onMsg(int msgType, NetlinkObject *netlinkObject); +// +// handle received net link messages +// +void NetMsgInterface::onMsg(int msgType, NetlinkObject *netlinkObject) +{ + static auto constexpr NONE_MAC = "none"; + + if ((msgType == RTM_NEWNEIGH) || (msgType == RTM_GETNEIGH) || (msgType == RTM_DELNEIGH)) { + RouteNetlinkNeighbor *routeNetlinkNeighbor = reinterpret_cast (netlinkObject); + if (rtnl_neigh_get_family(routeNetlinkNeighbor) == AF_INET || + rtnl_neigh_get_family(routeNetlinkNeighbor) == AF_INET6) { + std::string portName = swss::LinkCache::getInstance().ifindexToName( + rtnl_neigh_get_ifindex(routeNetlinkNeighbor) + ); + + std::array macStr; + nl_addr2str(rtnl_neigh_get_lladdr(routeNetlinkNeighbor), macStr.data(), macStr.size() - 1); + + if (strncmp(macStr.data(), NONE_MAC, strlen(NONE_MAC))) { + std::array ipStr; + nl_addr2str(rtnl_neigh_get_dst(routeNetlinkNeighbor), ipStr.data(), ipStr.size() - 1); + + MUXLOGDEBUG(boost::format("%s: interface IP '%s', MAC '%s', msgType %d") % + portName % ipStr.data() % macStr.data() % msgType + ); + + boost::system::error_code errorCode; + + boost::asio::ip::address ipAddress = boost::asio::ip::make_address(ipStr.data(), errorCode); + if (!errorCode) { + swss::MacAddress macAddress(macStr.data()); + mDbInterface.updateServerMacAddress(ipAddress, macAddress.getMac()); + } + } + } + } +} + +} /* namespace mux */ diff --git a/src/linkmgrd/src/NetMsgInterface.h b/src/linkmgrd/src/NetMsgInterface.h new file mode 100644 index 000000000000..2d730eb21125 --- /dev/null +++ b/src/linkmgrd/src/NetMsgInterface.h @@ -0,0 +1,73 @@ +/* + * NetMsgInterface.h + * + * Created on: Jan 12, 2021 + * Author: taahme + */ + +#ifndef _NETMSGINTERFACE_H_ +#define _NETMSGINTERFACE_H_ + +#include "swss/netmsg.h" +#include "DbInterface.h" + +namespace mux +{ +using NetlinkObject = struct nl_object; +using RouteNetlinkNeighbor = struct rtnl_neigh; +//using RouteNetlinkLink = struct rtnl_link; + +class NetMsgInterface: public swss::NetMsg +{ +public: + /** + *@method NetMsgInterface + * + *@brief class default constructor + */ + NetMsgInterface() = delete; + + /** + *@method NetMsgInterface + * + *@brief class copy constructor + * + *@param DbInterface (in) reference to DbInterface object to be copied + */ + NetMsgInterface(const NetMsgInterface &) = delete; + + /** + *@method NetMsgInterface + * + *@brief class constructor + * + *@param dbInterface (in) reference to DB interface instance + */ + NetMsgInterface(DbInterface &dbInterface); + + /** + *@method ~NetMsgInterface + * + *@brief class destructor + */ + virtual ~NetMsgInterface() = default; + + /** + *@method onMsg + * + *@brief handle received net link messages + * + *@param msgType (in) netlink message tyoe + *@param netlinkObject (in) pointer to netlink message object + * + *@return none + */ + virtual void onMsg(int msgType, NetlinkObject *netlinkObject) override; + +private: + DbInterface &mDbInterface; +}; + +} /* namespace mux */ + +#endif /* _NETMSGINTERFACE_H_ */ diff --git a/src/linkmgrd/src/common/MuxConfig.h b/src/linkmgrd/src/common/MuxConfig.h new file mode 100644 index 000000000000..16b796107b92 --- /dev/null +++ b/src/linkmgrd/src/common/MuxConfig.h @@ -0,0 +1,214 @@ +/* + * MuxConfig.h + * + * Created on: Oct 9, 2020 + * Author: tamer + */ + +#ifndef MUXCONFIG_H_ +#define MUXCONFIG_H_ + +#include + +#include + +namespace common +{ + +/** + *@class MuxConfig + * + *@brief Holds MUX configuration + */ +class MuxConfig +{ +public: + /** + *@method MuxConfig + * + *@brief class default constructor + */ + MuxConfig() = default; + + /** + *@method MuxConfig + * + *@brief class copy constructor + * + *@param MuxConfig (in) reference to MuxConfig object to be copied + */ + MuxConfig(const MuxConfig &) = delete; + + /** + *@method ~MuxConfig + * + *@brief class destructor + */ + virtual ~MuxConfig() = default; + + /** + *@method setNumberOfThreads + * + *@brief setter for number of threads + * + *@param numberOfThreads (in) number of threads of linkmgrd + * + *@return none + */ + inline void setNumberOfThreads(uint8_t numberOfThreads) {mNumberOfThreads = numberOfThreads;}; + + /** + *@method setTimeoutIpv4_msec + * + *@brief setter for IPv4 LinkProber timeout in msec + * + *@param timeout_msec (in) timeout in msec + * + *@return none + */ + inline void setTimeoutIpv4_msec(uint32_t timeout_msec) {mTimeoutIpv4_msec = timeout_msec;}; + + /** + *@method setTimeoutIpv6_msec + * + *@brief setter for IPv6 LinkProber timeout in msec + * + *@param timeout_msec (in) timeout in msec + * + *@return none + */ + inline void setTimeoutIpv6_msec(uint32_t timeout_msec) {mTimeoutIpv6_msec = timeout_msec;}; + + /** + *@method setStateChangeRetryCount + * + *@brief setter for LinkProber state change retry count + * + *@param stateChangeRetryCount (in) state change retry count + * + *@return none + */ + inline void setStateChangeRetryCount(uint32_t stateChangeRetryCount) {mStateChangeRetryCount = stateChangeRetryCount;}; + + /** + *@method setSuspendTimeout_msec + * + *@brief setter for LinkProber suspend timer timeout + * + *@param suspendTimeout_msec (in) suspend timer timeout + * + *@return none + */ + inline void setSuspendTimeout_msec(uint32_t suspendTimeout_msec) {mSuspendTimeout_msec = suspendTimeout_msec;}; + + /** + *@method setMuxStateChangeRetryCount + * + *@brief setter for MuxState state change retry count + * + *@param muxStateChangeRetryCount (in) state change retry count + * + *@return none + */ + inline void setMuxStateChangeRetryCount(uint32_t muxStateChangeRetryCount) {mMuxStateChangeRetryCount = muxStateChangeRetryCount;}; + + /** + *@method setLinkStateChangeRetryCount + * + *@brief setter for LinkeState change retry count + * + *@param linkStateChangeRetryCount (in) state change retry count + * + *@return none + */ + inline void setLinkStateChangeRetryCount(uint32_t linkStateChangeRetryCount) {mLinkStateChangeRetryCount = linkStateChangeRetryCount;}; + + /** + *@method setLoopbackIpv4Address + * + *@brief setter for Loopback IPv4 address + * + *@param address (in) IPv4 address + * + *@return none + */ + inline void setLoopbackIpv4Address(boost::asio::ip::address& address) {mLoopbackIpv4Address = address;}; + + /** + *@method getNumberOfThreads + * + *@brief getter for logging severity level + * + *@return number of linkmgrd/application threads + */ + inline uint8_t getNumberOfThreads() const {return mNumberOfThreads;}; + + /** + *@method getTimeoutIpv4_msec + * + *@brief getter for IPv4 LinkProber timeout in msec + * + *@return timeout in msec + */ + inline uint32_t getTimeoutIpv4_msec() const {return mTimeoutIpv4_msec;}; + + /** + *@method getStateChangeRetryCount + * + *@brief getter for LinkProber state change retry count + * + *@return state change retry count + */ + inline uint32_t getStateChangeRetryCount() const {return mStateChangeRetryCount;}; + + /** + *@method getSuspendTimeout_msec + * + *@brief getter for LinkProber suspend timer timeout + * + *@return suspend timer timeout + */ + inline uint32_t getSuspendTimeout_msec() const {return mSuspendTimeout_msec;}; + + /** + *@method getMuxStateChangeRetryCount + * + *@brief getter for MuxState state change retry count + * + *@return state change retry count + */ + inline uint32_t getMuxStateChangeRetryCount() const {return mMuxStateChangeRetryCount;}; + + /** + *@method getLinkStateChangeRetryCount + * + *@brief getter for LinkeState change retry count + * + *@return state change retry count + */ + inline uint32_t getLinkStateChangeRetryCount() const {return mLinkStateChangeRetryCount;}; + + /** + *@method getLoopbackIpv4Address + * + *@brief getter for Loopback IPv4 address + * + *@return IPv4 address + */ + inline boost::asio::ip::address getLoopbackIpv4Address() {return mLoopbackIpv4Address;}; + +private: + uint8_t mNumberOfThreads = 4; + uint32_t mTimeoutIpv4_msec = 100; + uint32_t mTimeoutIpv6_msec = 1000; + uint32_t mStateChangeRetryCount = 3; + uint32_t mSuspendTimeout_msec = 1000; + uint32_t mMuxStateChangeRetryCount = 1; + uint32_t mLinkStateChangeRetryCount = 1; + + boost::asio::ip::address mLoopbackIpv4Address = boost::asio::ip::make_address("10.212.64.0"); +}; + +} /* namespace common */ + +#endif /* MUXCONFIG_H_ */ diff --git a/src/linkmgrd/src/common/MuxException.h b/src/linkmgrd/src/common/MuxException.h new file mode 100644 index 000000000000..1963e4872522 --- /dev/null +++ b/src/linkmgrd/src/common/MuxException.h @@ -0,0 +1,137 @@ +/* + * MuxException.h + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#ifndef MUXEXCEPTION_H_ +#define MUXEXCEPTION_H_ + +#include +#include + +/** + * A macro for generating an MuxException with file path and line number. + */ +#define MUX_ERROR(name, msg) common::name##Exception(msg, __FILE__, __LINE__) + +namespace common +{ + +class MuxException : public std::exception { +public: + MuxException( + std::string excStr, + const char *excFile, + unsigned int excLine + ) noexcept + { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wterminate" + throw std::string(excFile) + ":" + std::to_string(excLine) + ": " + excStr; + #pragma GCC diagnostic pop + }; + + virtual ~MuxException () noexcept {}; +}; + +/** +* Exception for Runtime Expression +* +* This is thrown when a runtime exception is detected +*/ +class RunTimeErrorException : public MuxException { +public: + using MuxException::MuxException; + + virtual ~RunTimeErrorException () noexcept {}; +}; + +/** +* Exception for receiving invalid arguments. +* +* This is thrown when invalid arguments are received +*/ +class InvalidArgsException : public RunTimeErrorException { +public: + using RunTimeErrorException::RunTimeErrorException; + + virtual ~InvalidArgsException () noexcept {}; +}; + +/** +* Exception for MUX Logger +* +* This is thrown when boost logger report exceptions +*/ +class MuxLoggerException : public RunTimeErrorException { +public: + using RunTimeErrorException::RunTimeErrorException; + + virtual ~MuxLoggerException () noexcept {}; +}; + +/** +* Exception for Bad Memory Alloc +* +* This is thrown when failing to allocated memory +*/ +class BadAllocException : public RunTimeErrorException { +public: + using RunTimeErrorException::RunTimeErrorException; + + virtual ~BadAllocException () noexcept {}; +}; + +/** +* Exception for Socket Errors +* +* This is thrown when socket operation fails +*/ +class SocketErrorException : public RunTimeErrorException { +public: + using RunTimeErrorException::RunTimeErrorException; + + virtual ~SocketErrorException () noexcept {}; +}; + +/** +* Exception for File Not Found +* +* This is thrown when unsupported platform is detected +*/ +class FileNotFoundException : public RunTimeErrorException { +public: + using RunTimeErrorException::RunTimeErrorException; + + virtual ~FileNotFoundException () noexcept {}; +}; + +/** +* Exception for Internal Error +* +* This is thrown when unexpected behavior is detected. This should never happen. +*/ +class InternalErrorException : public MuxException { +public: + using MuxException::MuxException; + + virtual ~InternalErrorException () noexcept {}; +}; + +/** +* Exception for IO Error +* +* This is thrown when failing to read signal list file. +*/ +class IOErrorException : public MuxException { +public: + using MuxException::MuxException; + + virtual ~IOErrorException () noexcept {}; +}; + +} /* namespace common */ + +#endif /* MUXEXCEPTION_H_ */ diff --git a/src/linkmgrd/src/common/MuxLogger.cpp b/src/linkmgrd/src/common/MuxLogger.cpp new file mode 100644 index 000000000000..bc37d0f6262e --- /dev/null +++ b/src/linkmgrd/src/common/MuxLogger.cpp @@ -0,0 +1,147 @@ +/* + * MuxLogger.cpp + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MuxException.h" +#include "MuxLogger.h" + +namespace common +{ + +// +// ---> operator()(const boost::log::runtime_error &ex); +// +// handles runtime error exceptions +// +void MuxLoggerExceptionHandler::operator()(const boost::log::runtime_error &ex) const +{ + std::ostringstream errMsg; + errMsg << "MUX Logger exception!!" << ". Exception details: " << ex.what(); + + throw MUX_ERROR(MuxLogger, errMsg.str()); +} + +// +// ---> operator()(const std::exception &ex); +// +// handles std exceptions +// +void MuxLoggerExceptionHandler::operator()(const std::exception &ex) const +{ + std::ostringstream errMsg; + errMsg << "MUX Logger exception!!" << ". Exception details: " << ex.what(); + + throw MUX_ERROR(MuxLogger, errMsg.str()); +} + +// +// ---> getInstance(); +// +// constructs MuxLogger singleton instance +// +MuxLoggerPtr MuxLogger::getInstance() +{ + static std::shared_ptr MuxLoggerPtr = nullptr; + + if (MuxLoggerPtr == nullptr) { + MuxLoggerPtr = std::shared_ptr (new MuxLogger); + } + + return MuxLoggerPtr; +} + +// +// ---> initialize(std::string &prog, +// std::string &path, +// boost::log::trivial::severity_level level); +// +// initialize MUX logging class +// +void MuxLogger::initialize( + std::string &prog, + std::string &path, + boost::log::trivial::severity_level level +) +{ + namespace trivial = boost::log::trivial; + namespace keywords = boost::log::keywords; + + mLevel = level; + + boost::log::register_simple_formatter_factory ("Severity"); +// boost::filesystem::remove(path); + +// boost::log::add_file_log( +// keywords::file_name = path, +// keywords::format = "[%TimeStamp%] [%Severity%] %Message%" +// ); + + boost::log::core::get()->set_filter(trivial::severity >= level); + + boost::log::add_common_attributes(); + boost::log::core::get()->set_exception_handler( + boost::log::make_exception_handler (MuxLoggerExceptionHandler()) + ); + + addSyslogSink(prog); +} + +// +// ---> setLevel(const boost::log::trivial::severity_level level); +// +// set logger frontend and backend filter level +// +void MuxLogger::setLevel(const boost::log::trivial::severity_level level) +{ + namespace trivial = boost::log::trivial; + + mLevel = level; + boost::log::core::get()->set_filter(trivial::severity >= level); +} + +// +// ---> addSyslogSink(std::string &prog); +// +// Add syslog sink +// +void MuxLogger::addSyslogSink(std::string &prog) +{ + namespace sinks = boost::log::sinks; + namespace expressions = boost::log::expressions; + try { + // Create a syslog sink + boost::shared_ptr> sink( + new sinks::synchronous_sink () + ); + + sink->set_formatter(expressions::format(prog + ": %1%") % expressions::smessage); + + sink->locked_backend()->set_severity_mapper( + sinks::syslog::direct_severity_mapping ("Severity") + ); + + // Add the sink to the core + boost::log::core::get()->add_sink(sink); + } + catch (std::exception& ex) { + std::ostringstream errMsg; + errMsg << "MUX Logger exception!!" << ". Exception details: " << ex.what(); + + throw MUX_ERROR(MuxLogger, errMsg.str()); + } +} + +} /* namespace common */ diff --git a/src/linkmgrd/src/common/MuxLogger.h b/src/linkmgrd/src/common/MuxLogger.h new file mode 100644 index 000000000000..d93ada9817c6 --- /dev/null +++ b/src/linkmgrd/src/common/MuxLogger.h @@ -0,0 +1,223 @@ +/* + * MuxLogger.h + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#ifndef MUXLOGGER_H_ +#define MUXLOGGER_H_ + +#include + +#include +#include +#include +#include +#include + + +namespace common +{ +class MuxLogger; +using MuxLoggerPtr = std::shared_ptr; + +/** + *@class MuxLoggerExceptionHandler + * + *@brief Handles exception generated by boost logging framework + */ +class MuxLoggerExceptionHandler +{ +public: + /** + *@method operator() + * + *@brief handles runtime error exceptions + * + *@param ex (in) runtime error exception + * + *@throw MUX logger exception corresponding to exception received + */ + void operator()(const boost::log::runtime_error &ex) const; + + + /** + *@method operator() + * + *@brief handles std exceptions + * + *@param ex (in) std exception + * + *@throw MUX logger exception corresponding to exception received + */ + void operator()(const std::exception &ex) const; +}; + +/** + *@class MuxLogger + * + *@brief MUX Logger class wraps boost logging framework. It is a singleton + * class that manages MUX Logging + */ +class MuxLogger +{ +public: + /** + *@method MuxLogger + * + *@brief class copy constructor + * + *@param muxLogger (in) reference to muxLogger object to be copied + */ + MuxLogger(const MuxLogger &) = delete; + + /** + *@method ~MuxLogger + * + *@brief class destructor + * + */ + virtual ~MuxLogger() = default; + + /** + *@method getInstance + * + *@brief constructs MuxLogger singleton instance + * + *@return shared pointer to MuxLogger singleton instance + */ + static MuxLoggerPtr getInstance(); + + /** + *@method initialize + * + *@brief initialize MUX logging class + * + *@param prog (in) program name to be used when logging + *@param path (in) path on file system to MUX logging file + *@param level (in) minimum logging severity level + * + *@return none + */ + void initialize(std::string &prog, std::string &path, boost::log::trivial::severity_level level); + + /** + *@method setLevel + * + *@brief setter for logging severity level + * + *@param level (in) severity level for log messages that would follow + * + *@return none + */ + void setLevel(const boost::log::trivial::severity_level level); + + /** + *@method getLevel + * + *@brief getter for logging severity level + * + *@return current logging severity level + */ + boost::log::trivial::severity_level getLevel() const {return mLevel;}; + + /** + *@method getLogger + * + *@brief getter for Severity Logger + * + *@return severity logger instance + */ + boost::log::sources::severity_logger& + getLogger() {return mSeverityLogger;}; + +private: + friend class std::shared_ptr; + friend MuxLoggerPtr std::make_shared (); + + /** + *@method MuxLogger + * + *@brief class constructor + * + */ + MuxLogger() = default; + + void addSyslogSink(std::string &prog); + +private: + boost::log::trivial::severity_level mLevel = boost::log::trivial::info; + + boost::log::sources::severity_logger mSeverityLogger; +}; + +} /* namespace common */ + +#define XSTR(x) STR(x) +#define STR(x) #x + +/** + * MUXLOG macro prepends file name, line number, and function to logged message + * + *@param level (in) logging severity level + *@param msg (in) message to be logged + */ +#define MUXLOG(level, msg) \ + do { \ + if (level >= common::MuxLogger::getInstance()->getLevel()) { \ + BOOST_LOG_SEV(common::MuxLogger::getInstance()->getLogger(), level) \ + << XSTR(__FILENAME__) << ":" << __LINE__ << " " << __FUNCTION__ << ": " \ + << msg; \ + } \ + } while (0) + +/** + * MUXLOGTRACE handy macro that logs with trace severity level and appends file + * name, line number, function to the message being logged + * + *@param msg (in) message to be logged + */ +#define MUXLOGTRACE(msg) MUXLOG(boost::log::trivial::trace, msg) + +/** + * MUXLOGDEBUG handy macro that logs with debug severity level and appends file + * name, line number, function to the message being logged + * + *@param msg (in) message to be logged + */ +#define MUXLOGDEBUG(msg) MUXLOG(boost::log::trivial::debug, msg) + +/** + * MUXLOGINFO handy macro that logs with info severity level and appends file + * name, line number, function to the message being logged + * + *@param msg (in) message to be logged + */ +#define MUXLOGINFO(msg) MUXLOG(boost::log::trivial::info, msg) + +/** + * MUXLOGWARNING handy macro that logs with warning severity level and appends file + * name, line number, function to the message being logged + * + *@param msg (in) message to be logged + */ +#define MUXLOGWARNING(msg) MUXLOG(boost::log::trivial::warning, msg) + +/** + * MUXLOGERROR handy macro that logs with error severity level and appends file + * name, line number, function to the message being logged + * + *@param msg (in) message to be logged + */ +#define MUXLOGERROR(msg) MUXLOG(boost::log::trivial::error, msg) + +/** + * MUXLOGFATAL handy macro that logs with fatal severity level and appends file + * name, line number, function to the message being logged + * + *@param msg (in) message to be logged + */ +#define MUXLOGFATAL(msg) MUXLOG(boost::log::trivial::fatal, msg) + +#endif /* MUXLOGGER_H_ */ diff --git a/src/linkmgrd/src/common/MuxPortConfig.cpp b/src/linkmgrd/src/common/MuxPortConfig.cpp new file mode 100644 index 000000000000..78f0e1e4543b --- /dev/null +++ b/src/linkmgrd/src/common/MuxPortConfig.cpp @@ -0,0 +1,29 @@ +/* + * MuxPortConfig.cpp + * + * Created on: Oct 21, 2020 + * Author: tamer + */ + +#include "common/MuxPortConfig.h" + +namespace common +{ + +// +// ---> MuxPortConfig(MuxConfig &muxConfig, const std::string &portName, uint16_t serverId); +// +// class constructor +// +MuxPortConfig::MuxPortConfig( + MuxConfig &muxConfig, + const std::string &portName, + uint16_t serverId +) : + mMuxConfig(muxConfig), + mPortName(portName), + mServerId(serverId) +{ +} + +} /* namespace common */ diff --git a/src/linkmgrd/src/common/MuxPortConfig.h b/src/linkmgrd/src/common/MuxPortConfig.h new file mode 100644 index 000000000000..54a9b084ac32 --- /dev/null +++ b/src/linkmgrd/src/common/MuxPortConfig.h @@ -0,0 +1,220 @@ +/* + * MuxPortConfig.h + * + * Created on: Oct 21, 2020 + * Author: tamer + */ + +#ifndef MUXPORTCONFIG_H_ +#define MUXPORTCONFIG_H_ + +#include +#include +#include + +#include "MuxConfig.h" + +namespace common +{ + +/** + *@class MuxPortConfig + * + *@brief Holds MUX port configuration + */ +class MuxPortConfig +{ +public: + /** + *@enum Mode + * + *@brief MUX config mode + */ + enum Mode { + Auto, + Active + }; + +public: + /** + *@method MuxPortConfig + * + *@brief class default constructor + */ + MuxPortConfig() = delete; + + /** + *@method MuxPortConfig + * + *@brief class copy constructor + * + *@param MuxPortConfig (in) reference to MuxPortConfig object to be copied + */ + MuxPortConfig(const MuxPortConfig &) = delete; + + /** + *@method MuxPortConfig + * + *@brief class constructor + * + *@param muxConfig (in) reference to MuxConfig object + *@param portname (in) reference to port name + *@param serverId (in) server Id + */ + MuxPortConfig( + MuxConfig &muxConfig, + const std::string &portName, + uint16_t serverId + ); + + /** + *@method ~MuxPortConfig + * + *@brief class destructor + */ + virtual ~MuxPortConfig() = default; + + /** + *@method setBladeIpv4Address + * + *@brief setter for server/blade IPv4 address + * + *@param address (in) server IPv4 address + * + *@return none + */ + inline void setBladeIpv4Address(const boost::asio::ip::address &address) {mBladeIpv4Address = address;}; + + /** + *@method setBladeMacAddress + * + *@brief setter for server/blade MAC address + * + *@param address (in) server MAC address + * + *@return none + */ + inline void setBladeMacAddress(const std::array &address) {mBladeMacAddress = address;}; + + /** + *@method setMode + * + *@brief setter for MUX mode + * + *@param mode (in) MUX config mode + * + *@return none + */ + inline void setMode(const Mode mode) {mMode = mode;}; + + /** + *@method getTimeoutIpv4_msec + * + *@brief getter for IPv4 LinkProber timeout in msec + * + *@return timeout in msec + */ + inline uint32_t getTimeoutIpv4_msec() const {return mMuxConfig.getTimeoutIpv4_msec();}; + + /** + *@method getStateChangeRetryCount + * + *@brief getter for LinkProber state change retry count + * + *@return state change retry count + */ + inline uint32_t getStateChangeRetryCount() const {return mMuxConfig.getStateChangeRetryCount();}; + + /** + *@method getSuspendTimeout_msec + * + *@brief getter for LinkProber suspend timer timeout + * + *@return suspend timer timeout + */ + inline uint32_t getLinkWaitTimeout_msec() const {return mMuxConfig.getSuspendTimeout_msec();}; + + /** + *@method getMuxStateChangeRetryCount + * + *@brief getter for MuxState state change retry count + * + *@return state change retry count + */ + inline uint32_t getMuxStateChangeRetryCount() const {return mMuxConfig.getMuxStateChangeRetryCount();}; + + /** + *@method getLinkStateChangeRetryCount + * + *@brief getter for LinkeState change retry count + * + *@return state change retry count + */ + inline uint32_t getLinkStateChangeRetryCount() const {return mMuxConfig.getLinkStateChangeRetryCount();}; + + /** + *@method getLoopbackIpv4Address + * + *@brief getter for Loopback IPv4 address + * + *@return IPv4 address + */ + inline boost::asio::ip::address getLoopbackIpv4Address() const {return mMuxConfig.getLoopbackIpv4Address();}; + + /** + *@method getPortName + * + *@brief getter for port name + * + *@return port name + */ + inline const std::string& getPortName() const {return mPortName;}; + + /** + *@method getBladeIpv4Address + * + *@brief getter for server/blade IPv4 address + * + *@return IPv4 address + */ + inline const boost::asio::ip::address& getBladeIpv4Address() const {return mBladeIpv4Address;}; + + /** + *@method getBladeMacAddress + * + *@brief getter for server/blade MAC address + * + *@return IPv4 address + */ + inline const std::array& getBladeMacAddress() const {return mBladeMacAddress;}; + + /** + *@method getServerId + * + *@brief getter for server id + * + *@return server id + */ + inline uint16_t getServerId() const {return mServerId;}; + + /** + *@method getMode + * + *@brief getter for MUX mode + * + *@return MUX mode + */ + inline Mode getMode() const {return mMode;}; + +private: + MuxConfig &mMuxConfig; + std::string mPortName; + boost::asio::ip::address mBladeIpv4Address; + std::array mBladeMacAddress; + uint16_t mServerId; + Mode mMode = Auto; +}; + +} /* namespace common */ + +#endif /* MUXPORTCONFIG_H_ */ diff --git a/src/linkmgrd/src/common/State.cpp b/src/linkmgrd/src/common/State.cpp new file mode 100644 index 000000000000..95bd68559546 --- /dev/null +++ b/src/linkmgrd/src/common/State.cpp @@ -0,0 +1,28 @@ +/* + * State.cpp + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#include +#include "MuxLogger.h" + +namespace common +{ + +// +// ---> State(StateMachine &stateMachine, MuxPortConfig &muxPortConfig); +// +// class constructor +// +State::State( + StateMachine &stateMachine, + MuxPortConfig &muxPortConfig +) : + mStateMachine(stateMachine), + mMuxPortConfig(muxPortConfig) +{ +} + +} /* namespace common */ diff --git a/src/linkmgrd/src/common/State.h b/src/linkmgrd/src/common/State.h new file mode 100644 index 000000000000..15cddd6207ff --- /dev/null +++ b/src/linkmgrd/src/common/State.h @@ -0,0 +1,98 @@ +/* + * State.h + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#ifndef STATE_H_ +#define STATE_H_ + +#include +#include "common/MuxPortConfig.h" + +namespace common +{ +class StateMachine; + +/** + *@class State + * + *@brief Maintains common state functionality + */ +class State +{ +public: + /** + *@method State + * + *@brief class default constructor + */ + State() = delete; + + /** + *@method State + * + *@brief class copy constructor + * + *@param State (in) reference to State object to be copied + */ + State(const State &) = delete; + + /** + *@method State + * + *@brief class constructor + * + *@param stateMachine (in) reference to StateMachine object + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + State( + StateMachine &stateMachine, + MuxPortConfig &muxPortConfig + ); + + /** + *@method ~State + * + *@brief class destructor + */ + virtual ~State() = default; + + /** + *@method getStateMachine + * + *@brief getter for StateMachine object + * + *@return StateMacine object + */ + StateMachine* getStateMachine() {return &mStateMachine;}; + + /** + *@method getMuxPortConfig + * + *@brief getter for MuxPortConfig object + * + *@return MuxPortConfig object + */ + const MuxPortConfig& getMuxPortConfig() const {return mMuxPortConfig;}; + + /** + *@method resetState + * + *@brief reset current state attributes. It is called when transitioning into State. + * + *@param address (in) server IPv4 address + * + *@return none + */ + virtual void resetState() = 0; + +private: + StateMachine &mStateMachine; + MuxPortConfig &mMuxPortConfig; +}; + +} /* namespace common */ + +#endif /* STATE_H_ */ diff --git a/src/linkmgrd/src/common/StateMachine.cpp b/src/linkmgrd/src/common/StateMachine.cpp new file mode 100644 index 000000000000..9a8df8f99369 --- /dev/null +++ b/src/linkmgrd/src/common/StateMachine.cpp @@ -0,0 +1,43 @@ +/* + * StateMachine.cpp + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#include +#include +#include "MuxLogger.h" + + +namespace common +{ + +// +// ---> StateMachine(boost::asio::io_service::strand &strand, MuxPortConfig &muxPortConfig); +// +// class constructor +// +StateMachine::StateMachine( + boost::asio::io_service::strand &strand, + MuxPortConfig &muxPortConfig +) : + mStrand(strand), + mMuxPortConfig(muxPortConfig) +{ +} + +// +// ---> setCurrentState(common::State* state); +// +// setter for current state, reset state when changed +// +void StateMachine::setCurrentState(common::State* state) +{ + if (mCurrentState != state) { + mCurrentState = state; + mCurrentState->resetState(); + } +}; + +} /* namespace common */ diff --git a/src/linkmgrd/src/common/StateMachine.h b/src/linkmgrd/src/common/StateMachine.h new file mode 100644 index 000000000000..8101ff6c72e7 --- /dev/null +++ b/src/linkmgrd/src/common/StateMachine.h @@ -0,0 +1,134 @@ +/* + * StateMachine.h + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#ifndef STATEMACHINE_H_ +#define STATEMACHINE_H_ + +#include + +#include + +#include "common/MuxPortConfig.h" + +namespace link_manager { +class LinkManagerStateMachine; +} + +namespace link_prober { +class LinkProberStateMachine; +} + +namespace mux_state { +class MuxStateMachine; +} + +namespace link_state { +class LinkStateMachine; +} + +namespace common +{ +class State; + +/** + *@class StateMachine + * + *@brief Maintains common state machine functionality; current state, + * serialization object (strand,) and MuxPortConfig object + */ +class StateMachine +{ +public: + /** + *@method StateMachine + * + *@brief class default constructor + */ + StateMachine() = delete; + + /** + *@method StateMachine + * + *@brief class copy constructor + * + *@param StateMachine (in) reference to StateMachine object to be copied + */ + StateMachine(const StateMachine &) = delete; + + /** + *@method StateMachine + * + *@brief class constructor + * + *@param strand (in) boost serialization object + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + StateMachine( + boost::asio::io_service::strand &strand, + MuxPortConfig &muxPortConfig + ); + + /** + *@method ~StateMachine + * + *@brief class destructor + */ + virtual ~StateMachine() = default; + + /** + *@method getStrand + * + *@brief getter for boost serialization object + * + *@return reference to boost serialization object + */ + boost::asio::io_service::strand& getStrand() {return mStrand;}; + +private: + friend class link_manager::LinkManagerStateMachine; + friend class link_prober::LinkProberStateMachine; + friend class mux_state::MuxStateMachine; + friend class link_state::LinkStateMachine; + + /** + *@method setCurrentState + * + *@brief setter for current state + * + *@param state (in) current state of the state machine + * + *@return none + */ + void setCurrentState(State* state); + + /** + *@method getCurrentState + * + *@brief getter for current state + * + *@return current state of the state machine + */ + 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; + MuxPortConfig &mMuxPortConfig; +}; + +} /* namespace common */ + +#endif /* STATEMACHINE_H_ */ diff --git a/src/linkmgrd/src/common/subdir.mk b/src/linkmgrd/src/common/subdir.mk new file mode 100644 index 000000000000..9d0fc8d97cbd --- /dev/null +++ b/src/linkmgrd/src/common/subdir.mk @@ -0,0 +1,29 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./src/common/MuxLogger.cpp \ + ./src/common/MuxPortConfig.cpp \ + ./src/common/State.cpp \ + ./src/common/StateMachine.cpp + +OBJS += \ + ./src/common/MuxLogger.o \ + ./src/common/MuxPortConfig.o \ + ./src/common/State.o \ + ./src/common/StateMachine.o + +CPP_DEPS += \ + ./src/common/MuxLogger.d \ + ./src/common/MuxPortConfig.d \ + ./src/common/State.d \ + ./src/common/StateMachine.d + + +# Each subdirectory must supply rules for building sources it contributes +src/common/%.o: src/common/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/src/linkmgrd/src/link_manager/LinkManagerStateMachine.cpp b/src/linkmgrd/src/link_manager/LinkManagerStateMachine.cpp new file mode 100644 index 000000000000..a3db7ab7945f --- /dev/null +++ b/src/linkmgrd/src/link_manager/LinkManagerStateMachine.cpp @@ -0,0 +1,655 @@ +/* + * LinkManagerStateMachine.cpp + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#include + +#include "link_manager/LinkManagerStateMachine.h" +#include "common/MuxLogger.h" +#include "MuxPort.h" + +#define LOG_MUX_STATE_TRANSITION(portName, currentState, nextState) \ + do { \ + MUXLOGINFO(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) + +namespace link_manager +{ + +LinkManagerStateMachine::TransitionFunction + LinkManagerStateMachine::mStateTransitionHandler[link_prober::LinkProberState::Label::Count] + [mux_state::MuxState::Label::Count] + [link_state::LinkState::Label::Count]; + +LinkProberEvent LinkManagerStateMachine::mLinkProberEvent; +MuxStateEvent LinkManagerStateMachine::mMuxStateEvent; +LinkStateEvent LinkManagerStateMachine::mLinkStateEvent; + +std::vector LinkManagerStateMachine::mLinkProberStateName = {"Active", "Standby", "Unknown", "Wait"}; +std::vector LinkManagerStateMachine::mMuxStateName = {"Active", "Standby", "Unknown", "Wait"}; +std::vector LinkManagerStateMachine::mLinkStateName = {"Up", "Down"}; + +// +// ---> LinkManagerStateMachine( +// mux::MuxPort *muxPortPtr, +// boost::asio::io_service::strand &strand, +// common::MuxPortConfig &muxPortConfig +// ); +// +// class constructor +// +LinkManagerStateMachine::LinkManagerStateMachine( + mux::MuxPort *muxPortPtr, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig +) : + StateMachine(strand, muxPortConfig), + mMuxPortPtr(muxPortPtr), + mLinkProberStateMachine(*this, strand, muxPortConfig, ps(mCompositeState)), + mMuxStateMachine(*this, strand, muxPortConfig, ms(mCompositeState)), + mLinkStateMachine(*this, strand, muxPortConfig, ls(mCompositeState)) +{ + assert(muxPortPtr != nullptr); + mMuxStateMachine.setWaitStateCause(mux_state::WaitState::WaitStateCause::SwssUpdate); +} + +// +// ---> initializeTransitionFunctionTable() +// +// initialize static transition function table +// +void LinkManagerStateMachine::initializeTransitionFunctionTable() +{ + MUXLOGINFO("Initializing State Transition Table..."); + for (uint8_t linkProberState = link_prober::LinkProberState::Label::Active; + linkProberState < link_prober::LinkProberState::Label::Count; linkProberState++) { + for (uint8_t muxState = mux_state::MuxState::Label::Active; + muxState < mux_state::MuxState::Label::Count; muxState++) { + for (uint8_t linkState = link_state::LinkState::Label::Up; + linkState < link_state::LinkState::Label::Count; linkState++) { + mStateTransitionHandler[linkProberState][muxState][linkState] = + boost::bind( + &LinkManagerStateMachine::noopTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); + } + } + } + + mStateTransitionHandler[link_prober::LinkProberState::Label::Standby] + [mux_state::MuxState::Label::Active] + [link_state::LinkState::Label::Up] = + boost::bind( + &LinkManagerStateMachine::LinkProberStandbyMuxActiveLinkUpTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); + mStateTransitionHandler[link_prober::LinkProberState::Label::Unknown] + [mux_state::MuxState::Label::Active] + [link_state::LinkState::Label::Up] = + boost::bind( + &LinkManagerStateMachine::LinkProberUnknownMuxActiveLinkUpTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Standby] + [link_state::LinkState::Label::Up] = + boost::bind( + &LinkManagerStateMachine::LinkProberActiveMuxStandbyLinkUpTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); + mStateTransitionHandler[link_prober::LinkProberState::Label::Unknown] + [mux_state::MuxState::Label::Standby] + [link_state::LinkState::Label::Up] = + boost::bind( + &LinkManagerStateMachine::LinkProberUnknownMuxStandbyLinkUpTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Unknown] + [link_state::LinkState::Label::Up] = + boost::bind( + &LinkManagerStateMachine::LinkProberActiveMuxUnknownLinkUpTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); + mStateTransitionHandler[link_prober::LinkProberState::Label::Standby] + [mux_state::MuxState::Label::Unknown] + [link_state::LinkState::Label::Up] = + boost::bind( + &LinkManagerStateMachine::LinkProberStandbyMuxUnknownLinkUpTransitionFunction, + boost::placeholders::_1, + boost::placeholders::_2 + ); +// mStateTransitionHandler[link_prober::LinkProberState::Label::Active] +// [mux_state::MuxState::Label::Active] +// [link_state::LinkState::Label::Down] = +// boost::bind( +// &LinkManagerStateMachine::LinkProberActiveMuxActiveLinkDownTransitionFunction, +// boost::placeholders::_1, +// boost::placeholders::_2 +// ); +} + +// +// ---> enterLinkProberState(CompositeState &nextState, link_prober::LinkProberState::Label label); +// +// force LinkProberState to switch state +// +void LinkManagerStateMachine::enterLinkProberState(CompositeState &nextState, link_prober::LinkProberState::Label label) +{ + mLinkProberStateMachine.enterState(label); + ps(nextState) = label; +} + +// +// ---> enterMuxState(CompositeState &nextState, mux_state::MuxState::Label label); +// +// force MuxState to switch state +// +void LinkManagerStateMachine::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 LinkManagerStateMachine::enterLinkState(CompositeState &nextState, link_state::LinkState::Label label) +{ + mLinkStateMachine.enterState(label); + ls(nextState) = label; +} + +// +// ---> enterMuxWaitState(CompositeState &nextState); +// +// force MuxState to switch to WaitState +// +void LinkManagerStateMachine::enterMuxWaitState(CompositeState &nextState) +{ + enterMuxState(nextState, mux_state::MuxState::Label::Wait); + mMuxStateMachine.setWaitStateCause(mux_state::WaitState::WaitStateCause::DriverUpdate); + mMuxPortPtr->probeMuxState(); +} + +// +// ---> switchMuxState(CompositeState &nextState, mux_state::MuxState::Label label); +// +// switch Mux to switch via xcvrd to state label provider +// +void LinkManagerStateMachine::switchMuxState(CompositeState &nextState, mux_state::MuxState::Label label) +{ + enterMuxState(nextState, mux_state::MuxState::Label::Wait); + mMuxStateMachine.setWaitStateCause(mux_state::WaitState::WaitStateCause::SwssUpdate); + mMuxPortPtr->setMuxState(label); +} + +// +// ---> initializeLinkProber(); +// +// initialize LinkProber component. Note if this is the last component to be initialized, +// state machine will be activated +// +void LinkManagerStateMachine::initializeLinkProber() +{ + if (!mComponentInitState.test(LinkProberComponent)) { + mLinkProberPtr = std::make_shared ( + mMuxPortConfig, + getStrand().context(), + mLinkProberStateMachine + ); + mComponentInitState.set(LinkProberComponent); + + activateStateMachine(); + } +} + +// +// ---> activateStateMachine(); +// +// activate the state machine by starting the LinkProber. This should be done after all +// components have been initialized. +// +void LinkManagerStateMachine::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); + LOG_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + + mLinkProberPtr->initialize(); + mLinkProberPtr->startProbing(); + } +} + +// +// ---> handleStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state); +// +// handles LinkProverEvent +// +void LinkManagerStateMachine::handleStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state) +{ + if ((dynamic_cast (mLinkProberStateMachine.getCurrentState()))->getStateLabel() == state) { + MUXLOGINFO(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)](this, nextState); + LOG_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } +} + +// +// ---> handleStateChange(MuxStateEvent &event, mux_state::MuxState::Label state); +// +// handles MuxStateEvent +// +void LinkManagerStateMachine::handleStateChange(MuxStateEvent &event, mux_state::MuxState::Label state) +{ + if ((dynamic_cast (mMuxStateMachine.getCurrentState()))->getStateLabel() == state) { + MUXLOGINFO(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)](this, nextState); + LOG_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } + + if (ms(mCompositeState) != mux_state::MuxState::Wait) { + // Verify if state db MUX state matches the driver/current MUX state + mMuxPortPtr->getMuxState(); + } +} + +// +// ---> handleStateChange(LinkStateEvent &event, link_state::LinkState::Label state); +// +// handles LinkStateEvent +// +void LinkManagerStateMachine::handleStateChange(LinkStateEvent &event, link_state::LinkState::Label state) +{ + if ((dynamic_cast (mLinkStateMachine.getCurrentState()))->getStateLabel() == state) { + MUXLOGINFO(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) { + // start fresh when the link transition from Down to UP state + // and so the link prober will initially match the MUX state + // There is a problem with this approach as it will hide link flaps that result in lost heart-beats. + initLinkProberState(nextState); +// enterMuxWaitState(nextState); + } + else if (ls(mCompositeState) == link_state::LinkState::Up && + ls(nextState) == link_state::LinkState::Down && + ms(mCompositeState) == mux_state::MuxState::Label::Active && + mMuxPortConfig.getMode() == common::MuxPortConfig::Mode::Auto) { + // switch MUX to standby since we are entering LinkDown state + switchMuxState(nextState, mux_state::MuxState::Label::Standby); + } + else { + mStateTransitionHandler[ps(nextState)][ms(nextState)][ls(nextState)](this, nextState); + } + LOG_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } +} + +// +// ---> handleGetServerMacAddressNotification(std::array address); +// +// handle get Server MAC address +// +void LinkManagerStateMachine::handleGetServerMacAddressNotification(std::array address) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + if (!mComponentInitState.test(ServerMacComponent)) { + mMuxPortConfig.setBladeMacAddress(address); + + mComponentInitState.set(ServerMacComponent); + activateStateMachine(); + } +} + +// +// ---> handleGetMuxStateNotification(mux_state::MuxState::Label label); +// +// handle get MUX state notification +// +void LinkManagerStateMachine::handleGetMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(boost::format("%s: state db mux state: %d") % mMuxPortConfig.getPortName() % label); + + if (mComponentInitState.all() && ms(mCompositeState) != label && + ms(mCompositeState) != mux_state::MuxState::Wait) { + // notify swss of mux state change + MUXLOGWARNING(boost::format("%s: Switching MUX state from '%s' to '%s' to match linkmgrd/driver state") % + mMuxPortConfig.getPortName() % + mMuxStateName[label] % + mMuxStateName[ms(mCompositeState)] + ); + switchMuxState(mCompositeState, ms(mCompositeState)); + } +} + +// +// ---> handleProbeMuxStateNotification(mux_state::MuxState::Label label); +// +// handle MUX state notification. Source of notification could be state_db via +// orchagent or app_db via xcvrd +// +void LinkManagerStateMachine::handleProbeMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(boost::format("%s: state db mux state: %d") % mMuxPortConfig.getPortName() % label); + + if (mComponentInitState.all()) { + if (mMuxStateMachine.getWaitStateCause() != mux_state::WaitState::WaitStateCause::DriverUpdate || + ms(mCompositeState) != mux_state::MuxState::Wait) { + MUXLOGERROR(boost::format("%s: Received unsolicited MUX state probe notification!") % + mMuxPortConfig.getPortName() + ); + } + + postMuxStateEvent(label); + } else { + enterMuxState(mCompositeState, label); + + mComponentInitState.set(MuxStateComponent); + activateStateMachine(); + } +} + +// +// ---> handleMuxStateNotification(mux_state::MuxState::Label label); +// +// handle MUX state notification. Source of notification could be state_db via +// orchagent or app_db via xcvrd +// +void LinkManagerStateMachine::handleMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(boost::format("%s: state db mux state: %d") % mMuxPortConfig.getPortName() % label); + + if (mComponentInitState.all()) { + if (mMuxStateMachine.getWaitStateCause() != mux_state::WaitState::WaitStateCause::SwssUpdate || + ms(mCompositeState) != mux_state::MuxState::Wait) { + MUXLOGERROR(boost::format("%s: Received unsolicited MUX state change notification!") % + mMuxPortConfig.getPortName() + ); + } + + postMuxStateEvent(label); + } else { + enterMuxState(mCompositeState, label); + + mComponentInitState.set(MuxStateComponent); + activateStateMachine(); + } +} + +// +// ---> handleSwssLinkStateNotification(const link_state::LinkState::Label label); +// +// handle link state change notification +// +void LinkManagerStateMachine::handleSwssLinkStateNotification(const link_state::LinkState::Label label) +{ + MUXLOGDEBUG(boost::format("%s: state db link state: %d") % mMuxPortConfig.getPortName() % 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); + + mComponentInitState.set(LinkStateComponent); + activateStateMachine(); + } +} + +// +// ---> handleMuxConfigNotification(const common::MuxPortConfig::Mode mode); +// +// handle MUX configuration change notification +// +void LinkManagerStateMachine::handleMuxConfigNotification(const common::MuxPortConfig::Mode mode) +{ + if (mComponentInitState.all() && + mode == common::MuxPortConfig::Mode::Active && + ps(mCompositeState) == link_prober::LinkProberState::Label::Standby && + ms(mCompositeState) == mux_state::MuxState::Label::Standby) { + CompositeState nextState = mCompositeState; + enterLinkProberState(nextState, link_prober::LinkProberState::Wait); + switchMuxState(nextState, mux_state::MuxState::Label::Active); + LOG_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } +} + +// +// ---> handleSuspendTimerExpiry(); +// +// handle suspend timer expiry notification from LinkProber +// +void LinkManagerStateMachine::handleSuspendTimerExpiry() +{ + // Note: suspend timer is started when Mux state is active and link is in unknown state. + // If standby (peer) ToR fails to pull the link, this ToR will loop between those states + // Prober: Unknown, Mux: Active, Link: Up -> Wait, Active, UP --Timer Expires--> Unknown, Wait, UP -> + // Unknown, Active, Up + if (ps(mCompositeState) == link_prober::LinkProberState::Label::Wait) { + CompositeState nextState = mCompositeState; + // if we are still in wait state, enter an actionable state. This is required to handle + // a corner case where peer ToR switches MUX, however peer ToR is not sending probes +// enterLinkProberState(mCompositeState, link_prober::LinkProberState::Label::Unknown); + initLinkProberState(mCompositeState); + + enterMuxWaitState(nextState); + LOG_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } +} + +// +// ---> initLinkProberState(CompositeState &compositeState); +// +// initialize LinkProberState when configuring the composite state machine +// +void LinkManagerStateMachine::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::Standby); + break; + case mux_state::MuxState::Label::Unknown: + enterLinkProberState(compositeState, link_prober::LinkProberState::Label::Unknown); + break; + default: + break; + } +} + +// +// ---> postMuxStateEvent(mux_state::MuxState::Label label) +// +// post event to MUX state machine to change state +// +void LinkManagerStateMachine::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; + default: + break; + } +} + +// +// ---> noopTransitionFunction(CompositeState &nextState); +// +// No-op transition function +// +void LinkManagerStateMachine::noopTransitionFunction(CompositeState &nextState) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); +} + +// +// ---> LinkProberStandbyMuxActiveLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberUnknown, MuxActive, LinkUp} state +// +void LinkManagerStateMachine::LinkProberStandbyMuxActiveLinkUpTransitionFunction( + CompositeState &nextState +) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + // Probe the MUX state as internal MUX state is active while LP reports standby link + enterMuxWaitState(nextState); +} + +// +// ---> LinkProberUnknownMuxActiveLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberUnknown, MuxActive, LinkUp} state +// +void LinkManagerStateMachine::LinkProberUnknownMuxActiveLinkUpTransitionFunction( + CompositeState &nextState +) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + // trigger LINKMANAGER_CHECK and transition to LinkWait state, suspend prober + mLinkProberPtr->suspendTxProbes(mMuxPortConfig.getLinkWaitTimeout_msec()); + enterLinkProberState(nextState, link_prober::LinkProberState::Wait); + + enterMuxWaitState(nextState); +} + +// +// ---> LinkProberActiveMuxStandbyLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxStandby, LinkUp} state +// +void LinkManagerStateMachine::LinkProberActiveMuxStandbyLinkUpTransitionFunction( + CompositeState &nextState +) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + enterMuxWaitState(nextState); +} + +// +// ---> LinkProberUnknownMuxStandbyLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberUnknown, MuxStandby, LinkUp} state +// +void LinkManagerStateMachine::LinkProberUnknownMuxStandbyLinkUpTransitionFunction( + CompositeState &nextState +) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + enterLinkProberState(nextState, link_prober::LinkProberState::Wait); + + // Start switching MUX to active state as we lost HB from active ToR + switchMuxState(nextState, mux_state::MuxState::Label::Active); +} + +// +// ---> LinkProberActiveMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxUnknown, LinkUp} state +// +void LinkManagerStateMachine::LinkProberActiveMuxUnknownLinkUpTransitionFunction( + CompositeState &nextState +) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + enterMuxWaitState(nextState); +} + +// +// ---> LinkProberStandbyMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberStandby, MuxUnknown, LinkUp} state +// +void LinkManagerStateMachine::LinkProberStandbyMuxUnknownLinkUpTransitionFunction( + CompositeState &nextState +) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + enterMuxWaitState(nextState); +} + +// +// ---> LinkProberActiveMuxActiveLinkDownTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxActive, LinkDown} state +// +void LinkManagerStateMachine::LinkProberActiveMuxActiveLinkDownTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + + // Start switching MUX to standby as we lost the link + switchMuxState(nextState, mux_state::MuxState::Label::Standby); +} + +} /* namespace link_manager */ diff --git a/src/linkmgrd/src/link_manager/LinkManagerStateMachine.h b/src/linkmgrd/src/link_manager/LinkManagerStateMachine.h new file mode 100644 index 000000000000..3bd481f20d23 --- /dev/null +++ b/src/linkmgrd/src/link_manager/LinkManagerStateMachine.h @@ -0,0 +1,581 @@ +/* + * LinkManagerStateMachine.h + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#ifndef LINK_MANAGER_LINKMANAGERSTATEMACHINE_H_ +#define LINK_MANAGER_LINKMANAGERSTATEMACHINE_H_ + +#include +#include +#include +#include +#include + +#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 link_manager +{ + +/** + *@class LinkProberEvent + * + *@brief signals a LinkeProber event to the composite state machine + */ +class LinkProberEvent { +public: + LinkProberEvent() = default; + ~LinkProberEvent() = default; +}; + +/** + *@class MuxStateEvent + * + *@brief signals a MuxState event to the composite state machine + */ +class MuxStateEvent { +public: + MuxStateEvent() = default; + ~MuxStateEvent() = default; +}; + +/** + *@class LinkStateEvent + * + *@brief signals a LinkState event to the composite state machine + */ +class LinkStateEvent { +public: + LinkStateEvent() = default; + ~LinkStateEvent() = default; +}; + +/** + *@class LinkManagerStateMachine + * + *@brief Composite state machine of LinkProberState, MuxState, and LinkState + */ +class LinkManagerStateMachine: public common::StateMachine, + public std::enable_shared_from_this +{ +private: + /** + *@enum anonymous + * + *@brief used to reference bits corresponding to respective state machine init state + */ + enum { + ServerMacComponent, + LinkProberComponent, + MuxStateComponent, + LinkStateComponent, + + ComponentCount + }; + +public: + using CompositeState = std::tuple< + link_prober::LinkProberState::Label, + mux_state::MuxState::Label, + link_state::LinkState::Label + >; + using TransitionFunction = std::function; + +public: + /** + *@method LinkManagerStateMachine + * + *@brief class default constructor + */ + LinkManagerStateMachine() = delete; + + /** + *@method LinkManagerStateMachine + * + *@brief class copy constructor + * + *@param LinkManagerStateMachine (in) reference to LinkManagerStateMachine object to be copied + */ + LinkManagerStateMachine(const LinkManagerStateMachine &) = delete; + + /** + *@method LinkManagerStateMachine + * + *@brief class constructor + * + *@param muxPortPtr (in) pointer to container MuxPort object + *@param strand (in) boost serialization object + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + LinkManagerStateMachine( + mux::MuxPort *muxPortPtr, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~LinkManagerStateMachine + * + *@brief class destructor + */ + virtual ~LinkManagerStateMachine() = default; + + /** + *@method initializeTransitionFunctionTable + * + *@brief initialize static transition function table + * + *@return none + */ + static void initializeTransitionFunctionTable(); + + /** + *@method getLinkProberEvent + * + *@brief getter for LinkProberEvent object + * + *@return reference to LinkProberEvent object + */ + static LinkProberEvent& getLinkProberEvent() {return mLinkProberEvent;}; + + /** + *@method getMuxStateEvent + * + *@brief getter for MuxStateEvent object + * + *@return reference to MuxStateEvent object + */ + static MuxStateEvent& getMuxStateEvent() {return mMuxStateEvent;}; + + /** + *@method getLinkStateEvent + * + *@brief getter for LinkStateEvent object + * + *@return reference to LinkStateEvent object + */ + static LinkStateEvent& getLinkStateEvent() {return mLinkStateEvent;}; + + /** + *@method getCompositeState + * + *@brief getter for CompositeState object + * + *@return reference to CompositeState object + */ + const CompositeState& getCompositeState() {return mCompositeState;}; + + /** + *@method getLinkProberStateMachine + * + *@brief getter for LinkProberStateMachine object + * + *@return reference to LinkProberStateMachine object + */ + link_prober::LinkProberStateMachine& getLinkProberStateMachine() {return mLinkProberStateMachine;}; + + /** + *@method getMuxStateMachine + * + *@brief getter for MuxStateMachine object + * + *@return reference to MuxStateMachine object + */ + mux_state::MuxStateMachine& getMuxStateMachine() {return mMuxStateMachine;}; + + /** + *@method getLinkStateMachine + * + *@brief getter for LinkStateMachine object + * + *@return reference to LinkStateMachine object + */ + link_state::LinkStateMachine& getLinkStateMachine() {return mLinkStateMachine;}; + +private: + /** + *@method enterLinkProberState + * + *@brief force LinkProberState to switch state + * + *@param nextState (in, out) reference to composite state, the state linkProber + * entry will be changed to align with state label provided + *@param label (in) state to switch to + * + *@return none + */ + inline void enterLinkProberState(CompositeState &nextState, link_prober::LinkProberState::Label label); + + /** + *@method enterMuxState + * + *@brief force MuxState to switch state + * + *@param nextState (in, out) reference to composite state, the state MuxState + * entry will be changed to align with state label provided + *@param label (in) state to switch to + * + *@return none + */ + inline void enterMuxState(CompositeState &nextState, mux_state::MuxState::Label label); + + /** + *@method enterLinkState + * + *@brief force LinkState to switch state + * + *@param nextState (in, out) reference to composite state, the state LinkState + * entry will be changed to align with state label provided + *@param label (in) state to switch to + * + *@return none + */ + inline void enterLinkState(CompositeState &nextState, link_state::LinkState::Label label); + + /** + *@method enterMuxWaitState + * + *@brief force MuxState to switch to WaitState + * + *@param nextState (in, out) reference to composite state, the state MuxState + * entry will be changed to WaitState + * + *@return none + */ + inline void enterMuxWaitState(CompositeState &nextState); + + /** + *@method switchMuxState + * + *@brief switch Mux to switch via xcvrd to state label provider + * + *@param nextState (in, out) reference to composite state, the state MuxState + * entry will be changed to align with state label provided + *@param label (in) state to switch to + * + *@return none + */ + inline void switchMuxState(CompositeState &nextState, mux_state::MuxState::Label label); + +public: + /** + *@method initializeLinkProber + * + *@brief initialize LinkProber component. Note if this is the last component to be initialized, + * state machine will be activated + * + *@return none + */ + void initializeLinkProber(); + + /** + *@method activateStateMachine + * + *@brief activate the state machine by starting the LinkProber. This should be done after all + * components have been initialized. + * + *@return none + */ + void activateStateMachine(); + + /** + *@method handleStateChange + * + *@brief handles LinkProverEvent + * + *@param state (in) new LinkProberState label + * + *@return none + */ + void handleStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state); + + /** + *@method handleStateChange + * + *@brief handles MuxStateEvent + * + *@param state (in) new MuxState label + * + *@return none + */ + void handleStateChange(MuxStateEvent &event, mux_state::MuxState::Label state); + + /** + *@method handleStateChange + * + *@brief handles LinkStateEvent + * + *@param state (in) new LinkState label + * + *@return none + */ + void handleStateChange(LinkStateEvent &event, link_state::LinkState::Label state); + + /** + *@method handleGetServerMacNotification + * + *@brief handle get Server MAC address + * + *@param address (in) Server MAC address + * + *@return none + */ + void handleGetServerMacAddressNotification(std::array address); + + /** + *@method handleGetMuxStateNotification + * + *@brief handle get MUX state notification + * + *@param label (in) new MuxState label + * + *@return none + */ + void handleGetMuxStateNotification(mux_state::MuxState::Label label); + + /** + *@method handleProbeMuxStateNotification + * + *@brief handle probe MUX state notification + * + *@param label (in) new MuxState label + * + *@return none + */ + void handleProbeMuxStateNotification(mux_state::MuxState::Label label); + + /** + *@method handleMuxStateNotification + * + *@brief handle MUX state notification + * + *@param label (in) new MuxState label + * + *@return none + */ + void handleMuxStateNotification(mux_state::MuxState::Label label); + + /** + *@method handleSwssLinkStateNotification + * + *@brief handle link state change notification + * + *@param label (in) new LinkState label + * + *@return none + */ + void handleSwssLinkStateNotification(const link_state::LinkState::Label label); + + /** + *@method handleMuxConfigNotification + * + *@brief handle MUX configuration change notification + * + *@param mode (in) new MUX config mode + * + *@return none + */ + void handleMuxConfigNotification(const common::MuxPortConfig::Mode mode); + + /** + *@method handleSuspendTimerExpiry + * + *@brief handle suspend timer expiry notification from LinkProber + * + *@return none + */ + void handleSuspendTimerExpiry(); + +private: + /** + *@method initLinkProberState + * + *@brief initialize LinkProberState when configuring the composite state machine + * + *@param compositeState (in, out) reference to composite state, the state linkProber + * entry will be changed to align with MuxState + * + *@return none + */ + 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 + * + *@brief transition function when entering {LinkProberStandby, MuxActive, LinkUp} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberStandbyMuxActiveLinkUpTransitionFunction(CompositeState &nextState); + + /** + *@method LinkProberUnknownMuxActiveLinkUpTransitionFunction + * + *@brief transition function when entering {LinkProberUnknown, MuxActive, LinkUp} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberUnknownMuxActiveLinkUpTransitionFunction(CompositeState &nextState); + + /** + *@method LinkProberActiveMuxStandbyLinkUpTransitionFunction + * + *@brief transition function when entering {LinkProberActive, MuxStandby, LinkUp} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberActiveMuxStandbyLinkUpTransitionFunction(CompositeState &nextState); + + /** + *@method LinkProberUnknownMuxStandbyLinkUpTransitionFunction + * + *@brief transition function when entering {LinkProberUnknown, MuxStandby, LinkUp} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberUnknownMuxStandbyLinkUpTransitionFunction(CompositeState &nextState); + + /** + *@method LinkProberActiveMuxUnknownLinkUpTransitionFunction + * + *@brief transition function when entering {LinkProberActive, MuxUnknown, LinkUp} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberActiveMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); + + /** + *@method LinkProberStandbyMuxUnknownLinkUpTransitionFunction + * + *@brief transition function when entering {LinkProberStandby, MuxUnknown, LinkUp} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberStandbyMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); + + /** + *@method LinkProberActiveMuxActiveLinkDownTransitionFunction + * + *@brief transition function when entering {LinkProberActive, MuxActive, LinkDown} state + * + *@param nextState (in, out) reference to composite state, the state will be changed + * to reflect next new state. + * + *@return none + */ + void LinkProberActiveMuxActiveLinkDownTransitionFunction(CompositeState &nextState); + +private: + // This is used for testing... + friend class mux::MuxPort; + /** + *@method setLinkProberPtr + * + *@brief set new linkProber for the state machine. This method is used for testing + * + *@param linkProberPtr (in) pointer to LinkProber + * + *@return none + */ + void setLinkProberPtr(std::shared_ptr linkProberPtr) {mLinkProberPtr = linkProberPtr;}; + + /** + *@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);}; + +private: + static TransitionFunction mStateTransitionHandler[link_prober::LinkProberState::Label::Count] + [mux_state::MuxState::Label::Count] + [link_state::LinkState::Label::Count]; + + static LinkProberEvent mLinkProberEvent; + static MuxStateEvent mMuxStateEvent; + static LinkStateEvent mLinkStateEvent; + + // To print human readable state name + static std::vector mLinkProberStateName; + static std::vector mMuxStateName; + static std::vector mLinkStateName; + +private: + CompositeState mCompositeState = { + link_prober::LinkProberState::Label::Unknown, + mux_state::MuxState::Label::Wait, + link_state::LinkState::Label::Down + }; + + mux::MuxPort *mMuxPortPtr; + link_prober::LinkProberStateMachine mLinkProberStateMachine; + std::shared_ptr mLinkProberPtr = nullptr; + mux_state::MuxStateMachine mMuxStateMachine; + link_state::LinkStateMachine mLinkStateMachine; + + std::bitset mComponentInitState = {0}; +}; + +} /* namespace link_manager */ + +#endif /* LINK_MANAGER_LINKMANAGERSTATEMACHINE_H_ */ diff --git a/src/linkmgrd/src/link_manager/subdir.mk b/src/linkmgrd/src/link_manager/subdir.mk new file mode 100644 index 000000000000..232d246447e2 --- /dev/null +++ b/src/linkmgrd/src/link_manager/subdir.mk @@ -0,0 +1,20 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./src/link_manager/LinkManagerStateMachine.cpp + +OBJS += \ + ./src/link_manager/LinkManagerStateMachine.o + +CPP_DEPS += \ + ./src/link_manager/LinkManagerStateMachine.d + + +# Each subdirectory must supply rules for building sources it contributes +src/link_manager/%.o: src/link_manager/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/src/linkmgrd/src/link_prober/ActiveState.cpp b/src/linkmgrd/src/link_prober/ActiveState.cpp new file mode 100644 index 000000000000..ab08bc321209 --- /dev/null +++ b/src/linkmgrd/src/link_prober/ActiveState.cpp @@ -0,0 +1,107 @@ +/* + * ActiveState.cpp + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#include "link_prober/ActiveState.h" +#include "link_prober/StandbyState.h" +#include "link_prober/UnknownState.h" +#include "link_prober/LinkProberStateMachine.h" + +#include "common/MuxLogger.h" + +namespace link_prober +{ + +// +// ---> ActiveState(LinkProberStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +ActiveState::ActiveState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + LinkProberState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(IcmpPeerEvent &event); +// +// handle IcmpPeerEvent from LinkProber +// +LinkProberState* ActiveState::handleEvent(IcmpPeerEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mUnknownEventCount = 0; + if (++mPeerEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + else { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpSelfEvent &event); +// +// handle IcmpSelfEvent from LinkProber +// +LinkProberState* ActiveState::handleEvent(IcmpSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getActiveState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(IcmpUnknownEvent &event); +// +// handle IcmpUnknownEvent from LinkProber +// +LinkProberState* ActiveState::handleEvent(IcmpUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mPeerEventCount = 0; + if (++mUnknownEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUnknownState()); + } + else { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void ActiveState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mPeerEventCount = 0; + mUnknownEventCount = 0; +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/ActiveState.h b/src/linkmgrd/src/link_prober/ActiveState.h new file mode 100644 index 000000000000..00de25c457ba --- /dev/null +++ b/src/linkmgrd/src/link_prober/ActiveState.h @@ -0,0 +1,119 @@ +/* + * ActiveState.h + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_ACTIVESTATE_H_ +#define LINK_PROBER_ACTIVESTATE_H_ + +#include "LinkProberState.h" + +namespace link_prober +{ +class LinkProberStateMachine; + +/** + *@class ActiveState + * + *@brief maintains Active state of LinkProber + */ +class ActiveState: public LinkProberState +{ +public: + /** + *@method ActiveState + * + *@brief class default constructor + */ + ActiveState() = delete; + + /** + *@method ActiveState + * + *@brief class copy constructor + * + *@param ActiveState (in) reference to ActiveState object to be copied + */ + ActiveState(const ActiveState &) = delete; + + /** + *@method ActiveState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + ActiveState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~ActiveState + * + *@brief class destructor + */ + virtual ~ActiveState() = default; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerEvent from LinkProber + * + *@param event (in) reference to IcmpPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpSelfEvent from LinkProber + * + *@param event (in) reference to IcmpSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for LinkeProberState label + * + *@return LinkProberState Active label + */ + virtual LinkProberState::Label getStateLabel() override {return LinkProberState::Label::Active;}; + +private: + uint8_t mPeerEventCount = 0; + uint8_t mUnknownEventCount = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_ACTIVESTATE_H_ */ diff --git a/src/linkmgrd/src/link_prober/IcmpPayload.cpp b/src/linkmgrd/src/link_prober/IcmpPayload.cpp new file mode 100644 index 000000000000..6b1957a901cc --- /dev/null +++ b/src/linkmgrd/src/link_prober/IcmpPayload.cpp @@ -0,0 +1,50 @@ +/* + * IcmpPayload.cpp + * + * Created on: Oct 9, 2020 + * Author: tamer + */ + +#include + +#include +#include + +#include "IcmpPayload.h" +#include "common/MuxLogger.h" + +namespace link_prober +{ +// +// static members +// +boost::uuids::uuid IcmpPayload::mGuid; +uint32_t IcmpPayload::mCookie = 0x47656d69; +uint32_t IcmpPayload::mVersion = 0; + +// +// ---> IcmpPayload(); +// +// class constructor +// +IcmpPayload::IcmpPayload() : + cookie(htonl(mCookie)), + version(htonl(mVersion)) +{ + memcpy(un.uuid.data, mGuid.data, sizeof(un.uuid.data)); +} + +// +// ---> generateGuid() +// +// generate GUID for the current instance of linkmgrd +// +void IcmpPayload::generateGuid() +{ + boost::uuids::random_generator gen; + mGuid = gen(); + + MUXLOGINFO(boost::format("Link Prober generated GUID: {%s}") % boost::uuids::to_string(mGuid)); +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/IcmpPayload.h b/src/linkmgrd/src/link_prober/IcmpPayload.h new file mode 100644 index 000000000000..72e3f9b4cedc --- /dev/null +++ b/src/linkmgrd/src/link_prober/IcmpPayload.h @@ -0,0 +1,113 @@ +/* + * IcmpPayload.h + * + * Created on: Oct 9, 2020 + * Author: tamer + */ + +#ifndef ICMPPAYLOAD_H_ +#define ICMPPAYLOAD_H_ + +#pragma pack(push, 1) + +#include +#include +#include +#include +#include + +#include + +#include + +#define MUX_MAX_ICMP_BUFFER_SIZE 128 + +__BEGIN_DECLS + +namespace link_prober +{ + +/** + *@struct IcmpPayload + * + *@brief Build ICMP packet to sent to server + */ +struct IcmpPayload { + uint32_t cookie; + uint32_t version; + union { + uint64_t guid[2]; + boost::uuids::uuid uuid; + } un; + + /** + *@method IcmpPayload + * + *@brief class default constructor + */ + IcmpPayload(); + + /** + *@method generateGuid + * + *@brief generate GUID for the current instance of linkmgrd + * + *@return none + */ + static void generateGuid(); + + /** + *@method getGuidData + * + *@brief getter for GUID data + * + *@return pointer to current GUID data + */ + static uint8_t* getGuidData() {return reinterpret_cast (mGuid.data);}; + + /** + *@method getGuid + * + *@brief getter for GUID object + * + *@return reference to current GUID object + */ + static boost::uuids::uuid& getGuid() {return mGuid;}; + + /** + *@method getCookie + * + *@brief getter for ICMP cookie + * + *@return ICMP coolie + */ + static uint32_t getCookie() {return mCookie;}; + + /** + *@method getVersion + * + *@brief getter for current version + * + *@return current version + */ + static uint32_t getVersion() {return mVersion;}; + +private: + static uint32_t mCookie; + static uint32_t mVersion; + static boost::uuids::uuid mGuid; +} __attribute__((packed)); + +static_assert(sizeof(IcmpPayload) % 2 == 0, + "ICMP Payload size should be even sized, please add zero padding"); + +static_assert(sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload) < MUX_MAX_ICMP_BUFFER_SIZE, + "Buffer Size doesn't fit Link Prober ICMP packet with its payload"); + +} /* namespace link_prober */ + +__END_DECLS + +#pragma pack(pop) + +#endif /* ICMPPAYLOAD_H_ */ diff --git a/src/linkmgrd/src/link_prober/LinkProber.cpp b/src/linkmgrd/src/link_prober/LinkProber.cpp new file mode 100644 index 000000000000..00a5537dcbed --- /dev/null +++ b/src/linkmgrd/src/link_prober/LinkProber.cpp @@ -0,0 +1,457 @@ +/* + * LinkProber.cpp + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#include +#include +#include +#include + +#include + +#include "common/MuxLogger.h" +#include "LinkProber.h" +#include "common/MuxException.h" + +namespace link_prober +{ + +// +// Berkeley Packet Filter program that captures incoming ICMP traffic +// +SockFilter LinkProber::mIcmpFilter[] = { + [0] = { .code = 0x28, .jt = 0, .jf = 0, .k = 0x0000000c }, + [1] = { .code = 0x15, .jt = 0, .jf = 10, .k = 0x00000800 }, + [2] = { .code = 0x20, .jt = 0, .jf = 0, .k = 0x0000001a }, + [3] = { .code = 0x15, .jt = 0, .jf = 8, .k = 0x00000000 }, + [4] = { .code = 0x30, .jt = 0, .jf = 0, .k = 0x00000017 }, + [5] = { .code = 0x15, .jt = 0, .jf = 6, .k = 0x00000001 }, + [6] = { .code = 0x28, .jt = 0, .jf = 0, .k = 0x00000014 }, + [7] = { .code = 0x45, .jt = 4, .jf = 0, .k = 0x00001fff }, + [8] = { .code = 0xb1, .jt = 0, .jf = 0, .k = 0x0000000e }, + [9] = { .code = 0x50, .jt = 0, .jf = 0, .k = 0x0000000e }, + [10] = { .code = 0x15, .jt = 0, .jf = 1, .k = 0x00000000 }, + [11] = { .code = 0x6, .jt = 0, .jf = 0, .k = 0x00040000 }, + [12] = { .code = 0x6, .jt = 0, .jf = 0, .k = 0x00000000 }, +}; + +// +// ---> LinkProber( +// common::MuxPortConfig &muxPortConfig, +// boost::asio::io_service &ioService, +// LinkProberStateMachine &linkProberStateMachine +// ); +// +// class constructor +// +LinkProber::LinkProber( + common::MuxPortConfig &muxPortConfig, + boost::asio::io_service &ioService, + LinkProberStateMachine &linkProberStateMachine +) : + mMuxPortConfig(muxPortConfig), + mIoService(ioService), + mLinkProberStateMachine(linkProberStateMachine), + mStrand(mIoService), + mDeadlineTimer(mIoService), + mSuspendTimer(mIoService), + mStream(mIoService) +{ + try { + mSockFilterPtr = std::shared_ptr (new SockFilter[sizeof(mIcmpFilter) / sizeof(*mIcmpFilter)]); + memcpy(mSockFilterPtr.get(), mIcmpFilter, sizeof(mIcmpFilter)); + + mSockFilterProg.len = sizeof(mIcmpFilter) / sizeof(*mIcmpFilter); + mSockFilterProg.filter = mSockFilterPtr.get(); + } + catch (const std::bad_alloc& ex) { + std::ostringstream errMsg; + errMsg << "Failed allocate memory. Exception details: " << ex.what(); + + throw MUX_ERROR(BadAlloc, errMsg.str()); + } +} + +// +// ---> initialize(); +// +// initialize link prober sockets and builds ICMP packet +// +void LinkProber::initialize() +{ + mSocket = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMP); + if (mSocket < 0) { + std::ostringstream errMsg; + errMsg << "Failed to open socket with '" << strerror(errno) << "'" + << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + SockAddrLinkLayer addr; + memset(&addr, 0, sizeof(addr)); + + addr.sll_ifindex = if_nametoindex(mMuxPortConfig.getPortName().c_str()); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_ALL); + if (bind(mSocket, (struct sockaddr *) &addr, sizeof(addr))) { + std::ostringstream errMsg; + errMsg << "Failed to bind to interface '" << mMuxPortConfig.getPortName() << "' with '" + << strerror(errno) << "'" << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + mSockFilterPtr.get()[3].k = mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint(); + if (setsockopt(mSocket, SOL_SOCKET, SO_ATTACH_FILTER, &mSockFilterProg, sizeof(mSockFilterProg)) != 0) { + std::ostringstream errMsg; + errMsg << "Failed to attach filter with '" << strerror(errno) << "'" + << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + int fileDescriptor; + if ((fileDescriptor = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + std::ostringstream errMsg; + errMsg << "Failed to open socket with '" << strerror(errno) << "'" + << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + // Get ToR MAC address + ifreq ifRequest; + ifRequest.ifr_addr.sa_family = AF_INET; + strncpy(ifRequest.ifr_name, mMuxPortConfig.getPortName().c_str(), sizeof(ifRequest.ifr_name) - 1); + ifRequest.ifr_name[sizeof(ifRequest.ifr_name) - 1] = '\0'; + if (ioctl(fileDescriptor, SIOCGIFHWADDR, &ifRequest) == -1) { + std::ostringstream errMsg; + errMsg << "Failed to obtain hardware address for interface '" << mMuxPortConfig.getPortName() + << "' with '" << strerror(errno) << "'" << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + memcpy(mTorPortMac.data(), ifRequest.ifr_hwaddr.sa_data, mTorPortMac.size()); + + close(fileDescriptor); + + mStream.assign(mSocket); + + initializeSendBuffer(); +} + +// +// ---> startProbing(); +// +// suspend sending ICMP ECHOREQUEST packets +// +void LinkProber::startProbing() +{ + sendHeartbeat(); + startRecv(); + startTimer(); +} + +// +// ---> suspendTxProbes(uint32_t suspendTime_msec); +// +// suspend sending ICMP ECHOREQUEST packets +// +void LinkProber::suspendTxProbes(uint32_t suspendTime_msec) +{ + mSuspendTimer.expires_from_now(boost::posix_time::milliseconds(suspendTime_msec)); + mSuspendTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProber::handleSuspendTimeout, + this, + boost::asio::placeholders::error + ))); + + mSuspendTx = true; +} + +// +// ---> sendHeartbeat() +// +// send ICMP ECHOREQUEST packet +// +void LinkProber::sendHeartbeat() +{ + // check if suspend timer is running + if (!mSuspendTx) { + updateIcmpSequenceNo(); + boost::system::error_code errorCode; + mStream.write_some( + boost::asio::buffer( + mTxBuffer.data(), sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload) + ), + errorCode + ); + + if (errorCode) { + MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Failed to send heartbeat!"); + } else { + MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Done sending data"); + } + } +} + +// +// ---> handleRecv(const boost::system::error_code& errorCode, size_t bytesTransferred); +// +// handle packet reception +// +void LinkProber::handleRecv( + const boost::system::error_code& errorCode, + size_t bytesTransferred +) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + + if (errorCode != boost::asio::error::operation_aborted) { + iphdr *ipHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header)); + icmphdr *icmpHeader = reinterpret_cast ( + mRxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + ); + + IcmpPayload *icmpPayload = reinterpret_cast ( + mRxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + ); + + if (ntohl(icmpPayload->cookie) == IcmpPayload::getCookie() && + ntohl(icmpPayload->version) <= IcmpPayload::getVersion() && + ntohs(icmpHeader->un.echo.id) == mMuxPortConfig.getServerId()) { + MUXLOGTRACE(boost::format("%s: Valid ICMP Packet from %s") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getBladeIpv4Address().to_string() + ); + if (memcmp(icmpPayload->un.uuid.data, IcmpPayload::getGuidData(), + IcmpPayload::getGuid().size()) == 0) { + MUXLOGTRACE(boost::format("%s: Matching Guid") % mMuxPortConfig.getPortName()); + // echo reply for an echo request generated by this/active ToR + mRxSelfSeqNo = ntohs(icmpHeader->un.echo.sequence); + mRxPeerSeqNo = mTxSeqNo - 1; + } else { + // echo reply for an echo request generated by peer ToR + mRxPeerSeqNo = mTxSeqNo; + mRxSelfSeqNo = mTxSeqNo - 1; + } + } else { + // Unkonwn ICMP packet, ignore. + } + + MUXLOGTRACE(boost::format("%s: Got data from: %s, size: %d") % + mMuxPortConfig.getPortName() % + boost::asio::ip::address_v4(ntohl(ipHeader->saddr)).to_string() % + (bytesTransferred - sizeof(iphdr) - sizeof(ether_header)) + ); + // start another receive to consume as much as possible of backlog packets if any + startRecv(); + } +} + +// +// ---> handleTimeout(boost::system::error_code ec); +// +// handle ICMP packet reception timeout +// +void LinkProber::handleTimeout(boost::system::error_code errorCode) +{ + MUXLOGTRACE(boost::format("%s: server: %d, mRxSelfSeqNo: %d, mTxSeqNo: %d") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getServerId() % + mRxSelfSeqNo % + mTxSeqNo + ); + + mStream.cancel(); + if (mTxSeqNo == mRxSelfSeqNo) { + // post self event + mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpSelfEvent()); + } else if (mTxSeqNo == mRxPeerSeqNo) { + // post peer event + mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpPeerEvent()); + } else { + // post unknown event + mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpUnknownEvent()); + } + + // start another cycle of send/recv + startProbing(); +} + +// +// ---> handleSuspendTimeout(boost::system::error_code errorCode); +// +// handle suspend timer timeout +// +void LinkProber::handleSuspendTimeout(boost::system::error_code errorCode) +{ + mSuspendTx = false; + + // inform the composite state machine about Suspend timer expiry + boost::asio::io_service::strand& strand = mLinkProberStateMachine.getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&LinkProberStateMachine::processEvent), + &mLinkProberStateMachine, + LinkProberStateMachine::getSuspendTimerExpiredEvent() + ))); +} + +// +// ---> startRecv(); +// +// start ICMP ECHOREPLY reception +// +void LinkProber::startRecv() +{ + mStream.async_read_some( + boost::asio::buffer(mRxBuffer, MUX_MAX_ICMP_BUFFER_SIZE), + mStrand.wrap(boost::bind( + &LinkProber::handleRecv, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred + )) + ); +} + +// +// ---> startTimer(); +// +// start ICMP ECHOREPLY timeout timer +// +void LinkProber::startTimer() +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + // time out these hearbeats + mDeadlineTimer.expires_from_now(boost::posix_time::milliseconds(mMuxPortConfig.getTimeoutIpv4_msec())); + mDeadlineTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProber::handleTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> calculateChecksum(uint16_t *data, size_t size); +// +// calculate ICMP payload checksum +// +uint32_t LinkProber::calculateChecksum(uint16_t *data, size_t size) +{ + uint32_t sum = 0; + size_t offset = 0; + + do { + sum += ntohs(data[offset++]); + } while (offset < size); + + return sum; +} + +// +// ---> addChecksumCarryover(uint16_t *checksum, uint32_t sum); +// +// add checksum carryover +// +void LinkProber::addChecksumCarryover(uint16_t *checksum, uint32_t sum) +{ + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + *checksum = htons(~sum); +} + +// +// ---> computeChecksum(icmphdr *icmpHeader, size_t size); +// +// compute ICMP checksum +// +void LinkProber::computeChecksum(icmphdr *icmpHeader, size_t size) +{ + icmpHeader->checksum = 0; + mIcmpChecksum = calculateChecksum( + reinterpret_cast (icmpHeader), size / 2 + ); + addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); +} + +// +// ---> computeChecksum(iphdr *ipHeader, size_t size); +// +// compute IPv4 checksum +// +void LinkProber::computeChecksum(iphdr *ipHeader, size_t size) +{ + ipHeader->check = 0; + mIpChecksum = calculateChecksum( + reinterpret_cast (ipHeader), size / 2 + ); + addChecksumCarryover(&ipHeader->check, mIpChecksum); +} + +// +// ---> initializeSendBuffer(); +// +// initialize ICMP packet once +// +void LinkProber::initializeSendBuffer() +{ + ether_header *ethHeader = reinterpret_cast (mTxBuffer.data()); + memcpy(ethHeader->ether_dhost, mMuxPortConfig.getBladeMacAddress().data(), sizeof(ethHeader->ether_dhost)); + memcpy(ethHeader->ether_shost, mTorPortMac.data(), sizeof(ethHeader->ether_shost)); + ethHeader->ether_type = htons(ETHERTYPE_IP); + + iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + ipHeader->ihl = sizeof(iphdr) >> 2; + ipHeader->version = IPVERSION; + ipHeader->tos = 0; + ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload)); + ipHeader->id = static_cast (rand()); + ipHeader->frag_off = 0; + ipHeader->ttl = 64; + ipHeader->protocol = IPPROTO_ICMP; + ipHeader->check = 0; + ipHeader->saddr = htonl(mMuxPortConfig.getLoopbackIpv4Address().to_v4().to_uint()); + ipHeader->daddr = htonl(mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint()); + computeChecksum(ipHeader, ipHeader->ihl << 2); + + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + icmpHeader->type = ICMP_ECHO; + icmpHeader->code = 0; + icmpHeader->un.echo.id = htons(mMuxPortConfig.getServerId()); + icmpHeader->un.echo.sequence = htons(mTxSeqNo); + + IcmpPayload *icmpPayload = new (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)) IcmpPayload(); + computeChecksum(icmpHeader, sizeof(icmphdr) + sizeof(*icmpPayload)); +} + +// +// ---> updateIpSequenceNo(); +// +// update IP header checksum, used before sending new heartbeat +// +void LinkProber::updateIpSequenceNo() +{ + iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + uint16_t id = ipHeader->id; + ipHeader->id = static_cast (rand()); + + mIpChecksum += (id == ipHeader->id) ? 0 : (ipHeader->id - id); + addChecksumCarryover(&ipHeader->check, mIpChecksum); +} + +// +// ---> updateIcmpSequenceNo(); +// +// update ICMP packet checksum, used before sending new heartbeat +// +void LinkProber::updateIcmpSequenceNo() +{ + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + icmpHeader->un.echo.sequence = htons(++mTxSeqNo); + mIcmpChecksum += mTxSeqNo ? 1 : 0; + addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/LinkProber.h b/src/linkmgrd/src/link_prober/LinkProber.h new file mode 100644 index 000000000000..ba17384a15ca --- /dev/null +++ b/src/linkmgrd/src/link_prober/LinkProber.h @@ -0,0 +1,280 @@ +/* + * LinkProber.h + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#ifndef LINKPROBER_H_ +#define LINKPROBER_H_ + +#include +#include +#include +#include + +#include + +#include "IcmpPayload.h" +#include "LinkProberStateMachine.h" +#include "common/MuxPortConfig.h" + +namespace link_prober +{ +using SockFilter = struct sock_filter; +using SockFilterProg = struct sock_fprog; +using SockAddrLinkLayer = struct sockaddr_ll; + +/** + *@class LinkProber + * + *@brief probes the server sing ICMP ECHPREQUEST packet. The packet payload + * holds GUID that identifies this ToR. Reception of this ToR's GUID + * indicate that the link is in Active state. Reception of unknown + * GUID will indicate standby state. Lack of ICMP packets will signal + * that the link state is unknown. + */ +class LinkProber +{ +public: + /** + *@method LinkProber + * + *@brief class default constructor + */ + LinkProber() = delete; + + /** + *@method LinkProber + * + *@brief class copy constructor + * + *@param LinkProber (in) reference to LinkProber object to be copied + */ + LinkProber(const LinkProber &) = delete; + + /** + *@method LinkProber + * + *@brief class constructor + * + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param ioService (in) reference to boost io_service object + *@param linkProberStateMachine (in) reference to LinkProberStateMachine object + */ + LinkProber( + common::MuxPortConfig &muxPortConfig, + boost::asio::io_service &ioService, + LinkProberStateMachine &linkProberStateMachine + ); + + /** + *@method ~LinkProber + * + *@brief class destructor + */ + virtual ~LinkProber() = default; + + /** + *@method initialize + * + *@brief initialize link prober sockets and builds ICMP packet + * + *@return none + */ + void initialize(); + + /** + *@method startProbing + * + *@brief start probing server/blade using ICMP ECHOREQUEST + * + *@return none + */ + void startProbing(); + + /** + *@method suspendTxProbes + * + *@brief suspend sending ICMP ECHOREQUEST packets + * + *@return none + */ + void suspendTxProbes(uint32_t suspendTime_msec); + +private: + /** + *@method sendHeartbeat + * + *@brief send ICMP ECHOREQUEST packet + * + *@return none + */ + void sendHeartbeat(); + + /** + *@method handleRecv + * + *@brief handle packet reception + * + *@param errorCode (in) socket error code + *@param bytesTransferred (in) number of bytes received + * + *@return none + */ + void handleRecv( + const boost::system::error_code &errorCode, + size_t bytesTransferred + ); + + /** + *@method handleTimeout + * + *@brief handle ICMP packet reception timeout + * + *@param errorCode (in) socket error code + * + *@return none + */ + void handleTimeout(boost::system::error_code errorCode); + + /** + *@method handleSuspendTimeout + * + *@brief handle suspend timer timeout + * + *@param errorCode (in) socket error code + * + *@return none + */ + void handleSuspendTimeout(boost::system::error_code errorCode); + + /** + *@method startRecv + * + *@brief start ICMP ECHOREPLY reception + * + *@return none + */ + void startRecv(); + + /** + *@method startTimer + * + *@brief start ICMP ECHOREPLY timeout timer + * + *@return none + */ + void startTimer(); + + /** + *@method calculateChecksum + * + *@brief calculate ICMP payload checksum + * + *@param data (in) pointer to data buffer + *@param size (in) size of data buffer + * + *@return CRC checksum + */ + uint32_t calculateChecksum(uint16_t *data, size_t size); + + /** + *@method addChecksumCarryover + * + *@brief add checksum carryover + * + *@param checksum (out) pointer to checksum field + *@param sum (in) current sum of the buffer payload + * + *@return CRC checksum + */ + void addChecksumCarryover(uint16_t *checksum, uint32_t sum); + + /** + *@method computeChecksum + * + *@brief compute ICMP checksum + * + *@param icmpHeader (in, out) pointer ICMP header + *@param size (in) size of ICMP payload + * + *@return CRC checksum + */ + void computeChecksum(icmphdr *icmpHeader, size_t size); + + /** + *@method computeChecksum + * + *@brief compute IPv4 checksum + * + *@param ipHeader (in, out) pointer IPv4 header + *@param size (in) size of IPv4 header + * + *@return CRC checksum + */ + void computeChecksum(iphdr *ipHeader, size_t size); + + /** + *@method initializeSendBuffer + * + *@brief initialize ICMP packet once + * + *@return CRC checksum + */ + void initializeSendBuffer(); + + /** + *@method updateIpSequenceNo + * + *@brief update IP header checksum, used before sending new heartbeat + * + *@return CRC checksum + */ + void updateIpSequenceNo(); + + /** + *@method updateIcmpSequenceNo + * + *@brief update ICMP packet checksum, used before sending new heartbeat + * + *@return CRC checksum + */ + void updateIcmpSequenceNo(); + +private: + static SockFilter mIcmpFilter[]; + +private: + common::MuxPortConfig &mMuxPortConfig; + boost::asio::io_service &mIoService; + LinkProberStateMachine &mLinkProberStateMachine; + + uint16_t mTxSeqNo = 0xffff; + uint16_t mRxSelfSeqNo = 0; + uint16_t mRxPeerSeqNo = 0; + + uint32_t mIcmpChecksum = 0; + uint32_t mIpChecksum = 0; + + std::array mTorPortMac; + + boost::asio::io_service::strand mStrand; + boost::asio::deadline_timer mDeadlineTimer; + boost::asio::deadline_timer mSuspendTimer; + boost::asio::posix::stream_descriptor mStream; + + std::shared_ptr mSockFilterPtr; + SockFilterProg mSockFilterProg; + + int mSocket = 0; + + std::array mTxBuffer; + std::array mRxBuffer; + + bool mSuspendTx = false; +}; + +} /* namespace link_prober */ + +#endif /* LINKPROBER_H_ */ diff --git a/src/linkmgrd/src/link_prober/LinkProberState.cpp b/src/linkmgrd/src/link_prober/LinkProberState.cpp new file mode 100644 index 000000000000..69d2802d5f26 --- /dev/null +++ b/src/linkmgrd/src/link_prober/LinkProberState.cpp @@ -0,0 +1,31 @@ +/* + * LinkProberState.cpp + * + * Created on: Oct 8, 2020 + * Author: tamer + */ + + +#include +#include + +namespace link_prober +{ + +// +// ---> LinkProberState(LinkProberStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +LinkProberState::LinkProberState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + common::State( + *dynamic_cast (&stateMachine), + muxPortConfig + ) +{ +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/LinkProberState.h b/src/linkmgrd/src/link_prober/LinkProberState.h new file mode 100644 index 000000000000..20d8c5fcde03 --- /dev/null +++ b/src/linkmgrd/src/link_prober/LinkProberState.h @@ -0,0 +1,125 @@ +/* + * LinkProberState.h + * + * Created on: Oct 8, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_LINKPROBERSTATE_H_ +#define LINK_PROBER_LINKPROBERSTATE_H_ + +#include + +namespace link_prober +{ +class LinkProberStateMachine; +class IcmpPeerEvent; +class IcmpSelfEvent; +class IcmpUnknownEvent; +class SuspendTimerExpiredEvent; + +/** + *@class LinkProberState + * + *@brief base class for different LinkProber states + */ +class LinkProberState: public common::State +{ +public: + /** + *@enum Label + * + *@brief Label corresponding to each LinkProber State + */ + enum Label { + Active, + Standby, + Unknown, + Wait, + + Count + }; + +public: + /** + *@method LinkProberState + * + *@brief class default constructor + */ + LinkProberState() = delete; + + /** + *@method LinkProberState + * + *@brief class copy constructor + * + *@param LinkProberState (in) reference to LinkProberState object to be copied + */ + LinkProberState(const LinkProberState &) = delete; + + /** + *@method LinkProberState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachine object + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + LinkProberState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~LinkProber + * + *@brief class destructor + */ + virtual ~LinkProberState() = default; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerEvent from LinkProber + * + *@param event (in) reference to IcmpPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerEvent &event) = 0; + + /** + *@method handleEvent + * + *@brief handle IcmpSelfEvent from LinkProber + * + *@param event (in) reference to IcmpSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpSelfEvent &event) = 0; + + /** + *@method handleEvent + * + *@brief handle IcmpUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) = 0; + + /** + *@method getStateLabel + * + *@brief getter for LinkeProberState label + * + *@return LinkProberState Active label + */ + virtual LinkProberState::Label getStateLabel() = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_LINKPROBERSTATE_H_ */ diff --git a/src/linkmgrd/src/link_prober/LinkProberStateMachine.cpp b/src/linkmgrd/src/link_prober/LinkProberStateMachine.cpp new file mode 100644 index 000000000000..1c4cd2ccc66d --- /dev/null +++ b/src/linkmgrd/src/link_prober/LinkProberStateMachine.cpp @@ -0,0 +1,197 @@ +/* + * LinkProberStateMachine.cpp + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#include + +#include "link_prober/LinkProberStateMachine.h" +#include "link_manager/LinkManagerStateMachine.h" +#include "common/MuxLogger.h" +#include "LinkProberState.h" + +namespace link_prober +{ + +// +// static members +// +IcmpSelfEvent LinkProberStateMachine::mIcmpSelfEvent; +IcmpPeerEvent LinkProberStateMachine::mIcmpPeerEvent; +IcmpUnknownEvent LinkProberStateMachine::mIcmpUnknownEvent; +SuspendTimerExpiredEvent LinkProberStateMachine::mSuspendTimerExpiredEvent; + +// +// ---> LinkProberStateMachine( +// link_manager::LinkManagerStateMachine &linkManagerStateMachine, +// boost::asio::io_service::strand &strand, +// common::MuxPortConfig &muxPortConfig, +// LinkProberState::Label label +// ); +// +// class constructor +// +LinkProberStateMachine::LinkProberStateMachine( + link_manager::LinkManagerStateMachine &linkManagerStateMachine, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig, + LinkProberState::Label label +) : + StateMachine(strand, muxPortConfig), + mLinkManagerStateMachine(linkManagerStateMachine), + mActiveState(*this, muxPortConfig), + mStandbyState(*this, muxPortConfig), + mUnknownState(*this, muxPortConfig), + mWaitState(*this, muxPortConfig) +{ + enterState(label); +} + +// +// ---> enterState(LinkProberState::Label label); +// +// force the state machine to enter a given state +// +void LinkProberStateMachine::enterState(LinkProberState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + switch (label) { + case LinkProberState::Label::Active: + setCurrentState(dynamic_cast (getActiveState())); + break; + case LinkProberState::Label::Standby: + setCurrentState(dynamic_cast (getStandbyState())); + break; + case LinkProberState::Label::Unknown: + setCurrentState(dynamic_cast (getUnknownState())); + break; + case LinkProberState::Label::Wait: + setCurrentState(dynamic_cast (getWaitState())); + break; + default: + break; + } +} + +// +// ---> postLinkManagerEvent(LinkProberState* linkProberState); +// +// post LinkProberState change event to LinkManager state machine +// +inline +void LinkProberStateMachine::postLinkManagerEvent(LinkProberState* linkProberState) +{ + boost::asio::io_service::strand& strand = mLinkManagerStateMachine.getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&link_manager::LinkManagerStateMachine::handleStateChange), + &mLinkManagerStateMachine, + link_manager::LinkManagerStateMachine::getLinkProberEvent(), + linkProberState->getStateLabel() + ))); +} + +// +// ---> LinkProberStateMachine::postLinkProberStateEvent(E &e); +// +// post LinkProberState event to the state machine +// +template +void LinkProberStateMachine::postLinkProberStateEvent(E &e) +{ + boost::asio::io_service::strand& strand = getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&LinkProberStateMachine::processEvent), + this, + e + ))); +} + +// +// ---> LinkProberStateMachine::postLinkProberStateEvent(IcmpSelfEvent &e); +// +// post LinkProberState IcmpSelfEvent to the state machine +// +template +void LinkProberStateMachine::postLinkProberStateEvent(IcmpSelfEvent &event); + +// +// ---> LinkProberStateMachine::postLinkProberStateEvent(IcmpPeerEvent &e); +// +// post LinkProberState IcmpPeerEvent to the state machine +// +template +void LinkProberStateMachine::postLinkProberStateEvent(IcmpPeerEvent &event); + +// +// ---> LinkProberStateMachine::postLinkProberStateEvent(IcmpUnknownEvent &e); +// +// post LinkProberState IcmpUnknownEvent to the state machine +// +template +void LinkProberStateMachine::postLinkProberStateEvent(IcmpUnknownEvent &event); + +// +// ---> LinkProberStateMachine::processEvent(T &t); +// +// process LinkProberState event +// +template +void LinkProberStateMachine::processEvent(T &t) +{ + LinkProberState *currentLinkProberState = + dynamic_cast (getCurrentState()); + LinkProberState* nextLinkProberState = + currentLinkProberState->handleEvent(t); + if (nextLinkProberState != currentLinkProberState) { + postLinkManagerEvent(nextLinkProberState); + } + setCurrentState(nextLinkProberState); +} + +// +// ---> LinkProberStateMachine::processEvent(IcmpSelfEvent &t); +// +// process LinkProberState IcmpSelfEvent +// +template +void LinkProberStateMachine::processEvent(IcmpSelfEvent &event); + +// +// ---> LinkProberStateMachine::processEvent(IcmpPeerEvent &t); +// +// process LinkProberState IcmpPeerEvent +// +template +void LinkProberStateMachine::processEvent(IcmpPeerEvent &event); + +// +// ---> LinkProberStateMachine::processEvent(IcmpUnknownEvent &t); +// +// process LinkProberState IcmpUnknownEvent +// +template +void LinkProberStateMachine::processEvent(IcmpUnknownEvent &event); + +// +// ---> processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent); +// +// process LinkProberState suspend timer expiry event +// +void LinkProberStateMachine::processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent) +{ + boost::asio::io_service::strand& strand = mLinkManagerStateMachine.getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&link_manager::LinkManagerStateMachine::handleSuspendTimerExpiry), + &mLinkManagerStateMachine + ))); +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/LinkProberStateMachine.h b/src/linkmgrd/src/link_prober/LinkProberStateMachine.h new file mode 100644 index 000000000000..73a5080cdc9f --- /dev/null +++ b/src/linkmgrd/src/link_prober/LinkProberStateMachine.h @@ -0,0 +1,261 @@ +/* + * LinkProberStateMachine.h + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_LINKPROBERSTATEMACHINE_H_ +#define LINK_PROBER_LINKPROBERSTATEMACHINE_H_ + +#include +#include "link_prober/ActiveState.h" +#include "link_prober/StandbyState.h" +#include "link_prober/UnknownState.h" +#include "link_prober/WaitState.h" + +namespace link_manager { +class LinkManagerStateMachine; +} /* namespace link_manager */ + +namespace link_prober +{ +/** + *@class IcmpSelfEvent + * + *@brief signals a IcmpSelfEvent event to LinkProber state machine + */ +class IcmpSelfEvent { +public: + IcmpSelfEvent() = default; + ~IcmpSelfEvent() = default; +}; + +/** + *@class IcmpPeerEvent + * + *@brief signals a IcmpPeerEvent event to LinkProber state machine + */ +class IcmpPeerEvent { +public: + IcmpPeerEvent() = default; + ~IcmpPeerEvent() = default; +}; + +/** + *@class IcmpUnknownEvent + * + *@brief signals a IcmpUnknownEvent event to LinkProber state machine + */ +class IcmpUnknownEvent { +public: + IcmpUnknownEvent() = default; + ~IcmpUnknownEvent() = default; +}; + +/** + *@class SuspendTimerExpiredEvent + * + *@brief signals a SuspendTimerExpiredEvent event to LinkProber state machine + */ +class SuspendTimerExpiredEvent { +public: + SuspendTimerExpiredEvent() = default; + ~SuspendTimerExpiredEvent() = default; +}; + +/** + *@class LinkProberStateMachine + * + *@brief maintains LinkProber state machine + */ +class LinkProberStateMachine: public common::StateMachine +{ +public: + /** + *@method LinkProberStateMachine + * + *@brief class default constructor + */ + LinkProberStateMachine() = delete; + + /** + *@method LinkProberStateMachine + * + *@brief class copy constructor + * + *@param LinkProberStateMachine (in) reference to LinkProberStateMachine object to be copied + */ + LinkProberStateMachine(const LinkProberStateMachine &) = delete; + + /** + *@method LinkProberStateMachine + * + *@brief class constructor + * + *@param linkManagerStateMachine (in) reference to LinkManagerStateMachine + *@param strand (in) reference to boost serialization object + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param label (in) state machine initial state + */ + LinkProberStateMachine( + link_manager::LinkManagerStateMachine &linkManagerStateMachine, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig, + LinkProberState::Label label + ); + + /** + *@method ~LinkProberStateMachine + * + *@brief class destructor + */ + virtual ~LinkProberStateMachine() = default; + + /** + *@method enterState + * + *@brief force the state machine to enter a given state + * + *@param label (in) label of target state + * + *@return none + */ + void enterState(LinkProberState::Label label); + + /** + *@method postLinkProberStateEvent + * + *@brief post LinkProberState event to the state machine + * + *@param e (in) reference to the LinkProberState event + * + *@return none + */ + template + void postLinkProberStateEvent(E &e); + + /** + *@method processEvent + * + *@brief process LinkProberState event + * + *@param t (in) reference to the LinkProberState event + * + *@return none + */ + template + void processEvent(T &t); + + /** + *@method processEvent + * + *@brief process LinkProberState suspend timer expiry event + * + *@param suspendTimerExpiredEvent (in) reference to the SuspendTimerExpiredEvent event + * + *@return none + */ + void processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent); + + /** + *@method getActiveState + * + *@brief getter for ActiveState object + * + *@return pointer to ActiveState object + */ + ActiveState* getActiveState() {return &mActiveState;}; + + /** + *@method getStandbyState + * + *@brief getter for StandbyState object + * + *@return pointer to StandbyState object + */ + StandbyState* getStandbyState() {return &mStandbyState;}; + + /** + *@method getUnknownState + * + *@brief getter for UnknownState object + * + *@return pointer to UnknownState object + */ + UnknownState* getUnknownState() {return &mUnknownState;}; + + /** + *@method getWaitState + * + *@brief getter for WaitState object + * + *@return pointer to WaitState object + */ + WaitState* getWaitState() {return &mWaitState;}; + + /** + *@method getIcmpSelfEvent + * + *@brief getter for IcmpSelfEvent object + * + *@return pointer to IcmpSelfEvent object + */ + static IcmpSelfEvent& getIcmpSelfEvent() {return mIcmpSelfEvent;}; + + /** + *@method getIcmpPeerEvent + * + *@brief getter for IcmpPeerEvent object + * + *@return pointer to IcmpPeerEvent object + */ + static IcmpPeerEvent& getIcmpPeerEvent() {return mIcmpPeerEvent;}; + + /** + *@method getIcmpUnknownEvent + * + *@brief getter for IcmpUnknownEvent object + * + *@return pointer to IcmpUnknownEvent object + */ + static IcmpUnknownEvent& getIcmpUnknownEvent() {return mIcmpUnknownEvent;}; + + /** + *@method getSuspendTimerExpiredEvent + * + *@brief getter for SuspendTimerExpiredEvent object + * + *@return pointer to SuspendTimerExpiredEvent object + */ + static SuspendTimerExpiredEvent& getSuspendTimerExpiredEvent() {return mSuspendTimerExpiredEvent;}; + +private: + /** + *@method postLinkManagerEvent + * + *@brief post LinkProberState change event to LinkManager state machine + * + *@param linkProberState (in) pointer to current LinkProberState + * + *@return none + */ + inline void postLinkManagerEvent(LinkProberState* linkProberState); + +private: + static IcmpSelfEvent mIcmpSelfEvent; + static IcmpPeerEvent mIcmpPeerEvent; + static IcmpUnknownEvent mIcmpUnknownEvent; + static SuspendTimerExpiredEvent mSuspendTimerExpiredEvent; + +private: + link_manager::LinkManagerStateMachine &mLinkManagerStateMachine; + ActiveState mActiveState; + StandbyState mStandbyState; + UnknownState mUnknownState; + WaitState mWaitState; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_LINKPROBERSTATEMACHINE_H_ */ diff --git a/src/linkmgrd/src/link_prober/StandbyState.cpp b/src/linkmgrd/src/link_prober/StandbyState.cpp new file mode 100644 index 000000000000..f30aefde12e1 --- /dev/null +++ b/src/linkmgrd/src/link_prober/StandbyState.cpp @@ -0,0 +1,108 @@ +/* + * StandbyState.cpp + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#include "link_prober/ActiveState.h" +#include "link_prober/StandbyState.h" +#include "link_prober/UnknownState.h" +#include "link_prober/LinkProberStateMachine.h" + +#include "common/MuxLogger.h" + +namespace link_prober +{ + +// +// ---> StandbyState(LinkProberStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +StandbyState::StandbyState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + LinkProberState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(IcmpPeerEvent &event); +// +// handle IcmpPeerEvent from LinkProber +// +LinkProberState* StandbyState::handleEvent(IcmpPeerEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = + dynamic_cast (stateMachine->getStandbyState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(IcmpSelfEvent &event); +// +// handle IcmpSelfEvent from LinkProber +// +LinkProberState* StandbyState::handleEvent(IcmpSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mUnknownEventCount = 0; + if (++mSelfEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + else { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpUnknownEvent &event); +// +// handle IcmpUnknownEvent from LinkProber +// +LinkProberState* StandbyState::handleEvent(IcmpUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mSelfEventCount = 0; + if (++mUnknownEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUnknownState()); + } + else { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void StandbyState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mSelfEventCount = 0; + mUnknownEventCount = 0; +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/StandbyState.h b/src/linkmgrd/src/link_prober/StandbyState.h new file mode 100644 index 000000000000..e3a2d4c8b98e --- /dev/null +++ b/src/linkmgrd/src/link_prober/StandbyState.h @@ -0,0 +1,119 @@ +/* + * StandbyState.h + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_STANDBYSTATE_H_ +#define LINK_PROBER_STANDBYSTATE_H_ + +#include "LinkProberState.h" + +namespace link_prober +{ +class LinkProberStateMachine; + +/** + *@class StandbyState + * + *@brief maintains Standby state of LinkProber + */ +class StandbyState : public LinkProberState +{ +public: + /** + *@method StandbyState + * + *@brief class default constructor + */ + StandbyState() = delete; + + /** + *@method StandbyState + * + *@brief class copy constructor + * + *@param StandbyState (in) reference to StandbyState object to be copied + */ + StandbyState(const StandbyState &) = delete; + + /** + *@method StandbyState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + StandbyState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~StandbyState + * + *@brief class destructor + */ + virtual ~StandbyState() = default; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerEvent from LinkProber + * + *@param event (in) reference to IcmpPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpSelfEvent from LinkProber + * + *@param event (in) reference to IcmpSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for LinkeProberState label + * + *@return LinkProberState Standby label + */ + virtual LinkProberState::Label getStateLabel() override {return LinkProberState::Label::Standby;}; + +private: + uint8_t mSelfEventCount = 0; + uint8_t mUnknownEventCount = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_STANDBYSTATE_H_ */ diff --git a/src/linkmgrd/src/link_prober/UnknownState.cpp b/src/linkmgrd/src/link_prober/UnknownState.cpp new file mode 100644 index 000000000000..faa0e4a0e267 --- /dev/null +++ b/src/linkmgrd/src/link_prober/UnknownState.cpp @@ -0,0 +1,109 @@ +/* + * UnknownState.cpp + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#include "link_prober/ActiveState.h" +#include "link_prober/StandbyState.h" +#include "link_prober/UnknownState.h" +#include "link_prober/LinkProberStateMachine.h" + +#include "common/MuxLogger.h" + +namespace link_prober +{ + +// +// ---> UnknownState(LinkProberStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +UnknownState::UnknownState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + LinkProberState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(IcmpPeerEvent &event); +// +// handle IcmpPeerEvent from LinkProber +// +LinkProberState* UnknownState::handleEvent(IcmpPeerEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = + dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mSelfEventCount = 0; + if (++mPeerEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + else { + nextState = dynamic_cast (stateMachine->getUnknownState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpSelfEvent &event); +// +// handle IcmpSelfEvent from LinkProber +// +LinkProberState* UnknownState::handleEvent(IcmpSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = + dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mPeerEventCount = 0; + if (++mSelfEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + else { + nextState = dynamic_cast (stateMachine->getUnknownState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpUnknownEvent &event); +// +// handle IcmpUnknownEvent from LinkProber +// +LinkProberState* UnknownState::handleEvent(IcmpUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getUnknownState()); + + resetState(); + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void UnknownState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mSelfEventCount = 0; + mPeerEventCount = 0; +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/UnknownState.h b/src/linkmgrd/src/link_prober/UnknownState.h new file mode 100644 index 000000000000..4f2544b29e6d --- /dev/null +++ b/src/linkmgrd/src/link_prober/UnknownState.h @@ -0,0 +1,121 @@ +/* + * UnknownState.h + * + * Created on: Oct 7, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_UNKNOWNSTATE_H_ +#define LINK_PROBER_UNKNOWNSTATE_H_ + +#include + +#include "LinkProberState.h" + +namespace link_prober +{ +class LinkProberStateMachine; + +/** + *@class UnknownState + * + *@brief maintains Unknown state of LinkProber + */ +class UnknownState : public LinkProberState +{ +public: + /** + *@method UnknownState + * + *@brief class default constructor + */ + UnknownState() = delete; + + /** + *@method UnknownState + * + *@brief class copy constructor + * + *@param UnknownState (in) reference to UnknownState object to be copied + */ + UnknownState(const UnknownState &) = delete; + + /** + *@method UnknownState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + UnknownState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~UnknownState + * + *@brief class destructor + */ + virtual ~UnknownState() = default; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerEvent from LinkProber + * + *@param event (in) reference to IcmpPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpSelfEvent from LinkProber + * + *@param event (in) reference to IcmpSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpUnknownEvent &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::Unknown;}; + +private: + uint8_t mSelfEventCount = 0; + uint8_t mPeerEventCount = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_UNKNOWNSTATE_H_ */ diff --git a/src/linkmgrd/src/link_prober/WaitState.cpp b/src/linkmgrd/src/link_prober/WaitState.cpp new file mode 100644 index 000000000000..fcadce494ffc --- /dev/null +++ b/src/linkmgrd/src/link_prober/WaitState.cpp @@ -0,0 +1,107 @@ +/* + * WaitState.cpp + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#include "link_prober/ActiveState.h" +#include "link_prober/StandbyState.h" +#include "link_prober/WaitState.h" +#include "link_prober/LinkProberStateMachine.h" + +#include "common/MuxLogger.h" + +namespace link_prober +{ + +// +// ---> WaitState(LinkProberStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +WaitState::WaitState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + LinkProberState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(IcmpPeerEvent &event); +// +// handle IcmpPeerEvent from LinkProber +// +LinkProberState* WaitState::handleEvent(IcmpPeerEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mSelfEventCount = 0; + if (++mPeerEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + else { + nextState = dynamic_cast (stateMachine->getWaitState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpSelfEvent &event); +// +// handle IcmpSelfEvent from LinkProber +// +LinkProberState* WaitState::handleEvent(IcmpSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mPeerEventCount = 0; + if (++mSelfEventCount >= getMuxPortConfig().getStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + else { + nextState = dynamic_cast (stateMachine->getWaitState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpUnknownEvent &event); +// +// handle IcmpUnknownEvent from LinkProber +// +LinkProberState* WaitState::handleEvent(IcmpUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getWaitState()); + + resetState(); + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void WaitState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mSelfEventCount = 0; + mPeerEventCount = 0; +} + +} /* namespace link_prober */ diff --git a/src/linkmgrd/src/link_prober/WaitState.h b/src/linkmgrd/src/link_prober/WaitState.h new file mode 100644 index 000000000000..6f3a8e2a1c97 --- /dev/null +++ b/src/linkmgrd/src/link_prober/WaitState.h @@ -0,0 +1,121 @@ +/* + * WaitState.h + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_WAITSTATE_H_ +#define LINK_PROBER_WAITSTATE_H_ + +#include + +#include "LinkProberState.h" + +namespace link_prober +{ +class LinkProberStateMachine; + +/** + *@class WaitState + * + *@brief maintains Wait state of LinkProber + */ +class WaitState : public LinkProberState +{ +public: + /** + *@method WaitState + * + *@brief class default constructor + */ + WaitState() = delete; + + /** + *@method WaitState + * + *@brief class copy constructor + * + *@param WaitState (in) reference to WaitState object to be copied + */ + WaitState(const WaitState &) = delete; + + /** + *@method WaitState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + WaitState( + LinkProberStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~WaitState + * + *@brief class destructor + */ + virtual ~WaitState() = default; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerEvent from LinkProber + * + *@param event (in) reference to IcmpPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpSelfEvent from LinkProber + * + *@param event (in) reference to IcmpSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for LinkeProberState label + * + *@return LinkProberState Wait label + */ + virtual LinkProberState::Label getStateLabel() override {return LinkProberState::Label::Wait;}; + +private: + uint8_t mSelfEventCount = 0; + uint8_t mPeerEventCount = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_WAITSTATE_H_ */ diff --git a/src/linkmgrd/src/link_prober/subdir.mk b/src/linkmgrd/src/link_prober/subdir.mk new file mode 100644 index 000000000000..5250da268b92 --- /dev/null +++ b/src/linkmgrd/src/link_prober/subdir.mk @@ -0,0 +1,41 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./src/link_prober/ActiveState.cpp \ + ./src/link_prober/IcmpPayload.cpp \ + ./src/link_prober/LinkProber.cpp \ + ./src/link_prober/LinkProberState.cpp \ + ./src/link_prober/LinkProberStateMachine.cpp \ + ./src/link_prober/StandbyState.cpp \ + ./src/link_prober/UnknownState.cpp \ + ./src/link_prober/WaitState.cpp + +OBJS += \ + ./src/link_prober/ActiveState.o \ + ./src/link_prober/IcmpPayload.o \ + ./src/link_prober/LinkProber.o \ + ./src/link_prober/LinkProberState.o \ + ./src/link_prober/LinkProberStateMachine.o \ + ./src/link_prober/StandbyState.o \ + ./src/link_prober/UnknownState.o \ + ./src/link_prober/WaitState.o + +CPP_DEPS += \ + ./src/link_prober/ActiveState.d \ + ./src/link_prober/IcmpPayload.d \ + ./src/link_prober/LinkProber.d \ + ./src/link_prober/LinkProberState.d \ + ./src/link_prober/LinkProberStateMachine.d \ + ./src/link_prober/StandbyState.d \ + ./src/link_prober/UnknownState.d \ + ./src/link_prober/WaitState.d + + +# Each subdirectory must supply rules for building sources it contributes +src/link_prober/%.o: src/link_prober/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/src/linkmgrd/src/link_state/DownState.cpp b/src/linkmgrd/src/link_state/DownState.cpp new file mode 100644 index 000000000000..e99833f7d76c --- /dev/null +++ b/src/linkmgrd/src/link_state/DownState.cpp @@ -0,0 +1,79 @@ +/* + * DownState.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include "common/MuxLogger.h" +#include "link_state/DownState.h" +#include "link_state/UpState.h" +#include "link_state/LinkStateMachine.h" + +namespace link_state +{ + +// +// ---> DownState(LinkStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +DownState::DownState( + LinkStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + LinkState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(UpEvent &event); +// +// handle UpEvent from state db +// +LinkState* DownState::handleEvent(UpEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkState *nextState; + + if (++mUpEventCount >= getMuxPortConfig().getLinkStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUpState()); + } + else { + nextState = dynamic_cast (stateMachine->getDownState()); + } + + return nextState; +} + +// +// ---> handleEvent(DownEvent &event); +// +// handle DownEvent from state db +// +LinkState* DownState::handleEvent(DownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkState *nextState = dynamic_cast (stateMachine->getDownState()); + + resetState(); + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void DownState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mUpEventCount = 0; +} + +} /* namespace link_state */ diff --git a/src/linkmgrd/src/link_state/DownState.h b/src/linkmgrd/src/link_state/DownState.h new file mode 100644 index 000000000000..9c4dfa325882 --- /dev/null +++ b/src/linkmgrd/src/link_state/DownState.h @@ -0,0 +1,106 @@ +/* + * DownState.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef LINK_STATE_DOWNSTATE_H_ +#define LINK_STATE_DOWNSTATE_H_ + +#include + +namespace link_state +{ + +/** + *@class DownState + * + *@brief maintains DownState state of LinkState + */ +class DownState: public LinkState +{ +public: + /** + *@method DownState + * + *@brief class default constructor + */ + DownState() = delete; + + /** + *@method DownState + * + *@brief class copy constructor + * + *@param DownState (in) reference to DownState object to be copied + */ + DownState(const DownState &) = delete; + + /** + *@method DownState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + DownState( + LinkStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~DownState + * + *@brief class destructor + */ + virtual ~DownState() = default; + + /** + *@method handleEvent + * + *@brief handle UpEvent from state db + * + *@param event (in) reference to UpEvent + * + *@return pointer to next LinkState + */ + virtual LinkState* handleEvent(UpEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle DownEvent from state db + * + *@param event (in) reference to DownEvent + * + *@return pointer to next LinkState + */ + virtual LinkState* handleEvent(DownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for LinkState label + * + *@return LinkState Down label + */ + virtual LinkState::Label getStateLabel() override {return LinkState::Label::Down;}; + +private: + uint8_t mUpEventCount = 0; +}; + +} /* namespace link_state */ + +#endif /* LINK_STATE_DOWNSTATE_H_ */ diff --git a/src/linkmgrd/src/link_state/LinkState.cpp b/src/linkmgrd/src/link_state/LinkState.cpp new file mode 100644 index 000000000000..036e5020b1f7 --- /dev/null +++ b/src/linkmgrd/src/link_state/LinkState.cpp @@ -0,0 +1,30 @@ +/* + * LinkState.cpp + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#include +#include + +namespace link_state +{ + +// +// ---> LinkState(LinkStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +LinkState::LinkState( + LinkStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + common::State( + *dynamic_cast (&stateMachine), + muxPortConfig + ) +{ +} + +} /* namespace link_state */ diff --git a/src/linkmgrd/src/link_state/LinkState.h b/src/linkmgrd/src/link_state/LinkState.h new file mode 100644 index 000000000000..ba2319fd9fde --- /dev/null +++ b/src/linkmgrd/src/link_state/LinkState.h @@ -0,0 +1,110 @@ +/* + * LinkState.h + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#ifndef LINK_STATE_LINKSTATE_H_ +#define LINK_STATE_LINKSTATE_H_ + +#include + +namespace link_state +{ +class LinkStateMachine; +class UpEvent; +class DownEvent; + +/** + *@class LinkState + * + *@brief base class for different LinkState states + */ +class LinkState: public common::State +{ +public: + /** + *@enum Label + * + *@brief Label corresponding to each LinkState State + */ + enum Label { + Up, + Down, + + Count + }; + +public: + /** + *@method LinkState + * + *@brief class default constructor + */ + LinkState() = delete; + + /** + *@method LinkState + * + *@brief class copy constructor + * + *@param LinkState (in) reference to LinkState object to be copied + */ + LinkState(const LinkState &) = delete; + + /** + *@method LinkState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachine object + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + LinkState( + LinkStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~LinkProber + * + *@brief class destructor + */ + virtual ~LinkState() = default; + + /** + *@method handleEvent + * + *@brief handle UpEvent from state db + * + *@param event (in) reference to UpEvent + * + *@return pointer to next LinkState + */ + virtual LinkState* handleEvent(UpEvent &event) = 0; + + /** + *@method handleEvent + * + *@brief handle DownEvent from state db + * + *@param event (in) reference to DownEvent + * + *@return pointer to next LinkState + */ + virtual LinkState* handleEvent(DownEvent &event) = 0; + + /** + *@method getStateLabel + * + *@brief getter for LinkState label + * + *@return LinkState Down label + */ + virtual LinkState::Label getStateLabel() = 0; +}; + +} /* namespace link_state */ + +#endif /* LINK_STATE_LINKSTATE_H_ */ diff --git a/src/linkmgrd/src/link_state/LinkStateMachine.cpp b/src/linkmgrd/src/link_state/LinkStateMachine.cpp new file mode 100644 index 000000000000..2504f0d50b75 --- /dev/null +++ b/src/linkmgrd/src/link_state/LinkStateMachine.cpp @@ -0,0 +1,154 @@ +/* + * LinkStateMachine.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include + +#include "common/MuxLogger.h" +#include "link_state/LinkStateMachine.h" +#include "link_manager/LinkManagerStateMachine.h" + + +namespace link_state +{ +// +// static members +// +UpEvent LinkStateMachine::mUpEvent; +DownEvent LinkStateMachine::mDownEvent; + +// +// ---> LinkStateMachine( +// link_manager::LinkManagerStateMachine &linkManagerStateMachine, +// boost::asio::io_service::strand &strand, +// common::MuxPortConfig &muxPortConfig, +// LinkState::Label label +// ); +// +// class constructor +// +LinkStateMachine::LinkStateMachine( + link_manager::LinkManagerStateMachine &linkManagerStateMachine, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig, + LinkState::Label label +) : + common::StateMachine(strand, muxPortConfig), + mLinkManagerStateMachine(linkManagerStateMachine), + mUpState(*this, muxPortConfig), + mDownState(*this, muxPortConfig) +{ + enterState(label); +} + +// +// ---> enterState(LinkState::Label label); +// +// force the state machine to enter a given state +// +void LinkStateMachine::enterState(LinkState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + switch (label) { + case LinkState::Label::Up: + setCurrentState(dynamic_cast (getUpState())); + break; + case LinkState::Label::Down: + setCurrentState(dynamic_cast (getDownState())); + break; + default: + break; + } +} + +// +// ---> postLinkManagerEvent(LinkState* linkState); +// +// post LinkState change event to LinkManager state machine +// +inline +void LinkStateMachine::postLinkManagerEvent(LinkState* linkState) +{ + boost::asio::io_service::strand& strand = mLinkManagerStateMachine.getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&link_manager::LinkManagerStateMachine::handleStateChange), + &mLinkManagerStateMachine, + link_manager::LinkManagerStateMachine::getLinkStateEvent(), + linkState->getStateLabel() + ))); +} + +// +// ---> LinkStateMachine::postLinkStateEvent(E &e); +// +// post LinkState event to the state machine +// +template +void LinkStateMachine::postLinkStateEvent(E &e) +{ + boost::asio::io_service::strand& strand = getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&LinkStateMachine::processEvent), + this, + e + ))); +} + +// +// ---> postLinkStateEvent(UpEvent &e); +// +// post LinkState event to the state machine +// +template +void LinkStateMachine::postLinkStateEvent(UpEvent &e); + +// +// ---> postLinkStateEvent(DownEvent &e); +// +// post LinkState event to the state machine +// +template +void LinkStateMachine::postLinkStateEvent(DownEvent &e); + +// +// ---> processEvent(T &t); +// +// process LinkState event +// +template +void LinkStateMachine::processEvent(T &t) +{ + LinkState *currentLinkState = + dynamic_cast (getCurrentState()); + LinkState* nextLinkState = + currentLinkState->handleEvent(t); + if (nextLinkState != currentLinkState) { + postLinkManagerEvent(nextLinkState); + } + setCurrentState(nextLinkState); +} + +// +// ---> processEvent(UpEvent &event); +// +// process LinkState event +// +template +void LinkStateMachine::processEvent(UpEvent &event); + +// +// ---> processEvent(DownEvent &event); +// +// process LinkState event +// +template +void LinkStateMachine::processEvent(DownEvent &event); + +} /* namespace link_state */ diff --git a/src/linkmgrd/src/link_state/LinkStateMachine.h b/src/linkmgrd/src/link_state/LinkStateMachine.h new file mode 100644 index 000000000000..3561db1381f2 --- /dev/null +++ b/src/linkmgrd/src/link_state/LinkStateMachine.h @@ -0,0 +1,186 @@ +/* + * LinkStateMachine.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef LINK_STATE_LINKSTATEMACHINE_H_ +#define LINK_STATE_LINKSTATEMACHINE_H_ + +#include "common/StateMachine.h" +#include "DownState.h" +#include "UpState.h" + +namespace link_manager { +class LinkManagerStateMachine; +} /* namespace link_manager */ + +namespace link_state +{ +/** + *@class UpEvent + * + *@brief signals a UpEvent event to LinkState state machine + */ +class UpEvent { +public: + UpEvent() = default; + ~UpEvent() = default; +}; + +/** + *@class DownEvent + * + *@brief signals a DownEvent event to LinkState state machine + */ +class DownEvent { +public: + DownEvent() = default; + ~DownEvent() = default; +}; + +/** + *@class LinkStateMachine + * + *@brief maintains LineState state machine + */ +class LinkStateMachine: public common::StateMachine +{ +public: + /** + *@method LinkStateMachine + * + *@brief class default constructor + */ + LinkStateMachine() = delete; + + /** + *@method LinkStateMachine + * + *@brief class copy constructor + * + *@param LinkStateMachine (in) reference to LinkStateMachine object to be copied + */ + LinkStateMachine(const LinkStateMachine &) = delete; + + /** + *@method LinkStateMachine + * + *@brief class constructor + * + *@param linkManagerStateMachine (in) reference to LinkManagerStateMachine + *@param strand (in) reference to boost serialization object + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param label (in) state machine initial state + */ + LinkStateMachine( + link_manager::LinkManagerStateMachine &linkManagerStateMachine, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig, + LinkState::Label label + ); + + /** + *@method ~LinkStateMachine + * + *@brief class destructor + */ + virtual ~LinkStateMachine() = default; + + /** + *@method enterState + * + *@brief force the state machine to enter a given state + * + *@param label (in) label of target state + * + *@return none + */ + void enterState(LinkState::Label label); + + /** + *@method postLinkStateEvent + * + *@brief post LinkState event to the state machine + * + *@param e (in) reference to the LinkState event + * + *@return none + */ + template + void postLinkStateEvent(E &e); + + /** + *@method processEvent + * + *@brief process LinkState event + * + *@param t (in) reference to the LinkState event + * + *@return none + */ + template + void processEvent(T &t); + + /** + *@method getUpState + * + *@brief getter for UpState object + * + *@return pointer to UpState object + */ + UpState* getUpState() {return &mUpState;}; + + /** + *@method getDownState + * + *@brief getter for DownState object + * + *@return pointer to DownState object + */ + DownState* getDownState() {return &mDownState;}; + + /** + *@method getUpEvent + * + *@brief getter for UpEvent object + * + *@return pointer to UpEvent object + */ + static UpEvent& getUpEvent() {return mUpEvent;}; + + /** + *@method getDownEvent + * + *@brief getter for DownEvent object + * + *@return pointer to DownEvent object + */ + static DownEvent& getDownEvent() {return mDownEvent;}; + +private: + /** + *@method postLinkManagerEvent + * + *@brief post LinkState change event to LinkManager state machine + * + *@param LinkState (in) pointer to current LinkState + * + *@return none + */ + inline void postLinkManagerEvent(LinkState* linkState); + +private: + static UpEvent mUpEvent; + static DownEvent mDownEvent; + +private: + link_manager::LinkManagerStateMachine &mLinkManagerStateMachine; + UpState mUpState; + DownState mDownState; +}; + +} /* namespace link_state */ + +#endif /* LINK_STATE_LINKSTATEMACHINE_H_ */ diff --git a/src/linkmgrd/src/link_state/UpState.cpp b/src/linkmgrd/src/link_state/UpState.cpp new file mode 100644 index 000000000000..5f2efda2be48 --- /dev/null +++ b/src/linkmgrd/src/link_state/UpState.cpp @@ -0,0 +1,79 @@ +/* + * UpState.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include "common/MuxLogger.h" +#include "link_state/DownState.h" +#include "link_state/UpState.h" +#include "link_state/LinkStateMachine.h" + +namespace link_state +{ + +// +// ---> UpState(LinkStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +UpState::UpState( + LinkStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + LinkState(stateMachine, muxPortConfig) +{ +} + +// +// --->handleEvent(UpEvent &event); +// +// handle UpEvent from state db +// +LinkState* UpState::handleEvent(UpEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkState *nextState = dynamic_cast (stateMachine->getUpState()); + + resetState(); + + return nextState; +} + +// +// --->handleEvent(DownEvent &event); +// +// handle UpEvent from state db +// +LinkState* UpState::handleEvent(DownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkStateMachine *stateMachine = dynamic_cast (getStateMachine()); + LinkState *nextState; + + if (++mDownEventCount >= getMuxPortConfig().getLinkStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getDownState()); + } + else { + nextState = dynamic_cast (stateMachine->getUpState()); + } + + return nextState; +} + +// +// --->resetState(); +// +// reset current state attributes +// +void UpState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mDownEventCount = 0; +} + +} /* namespace link_state */ diff --git a/src/linkmgrd/src/link_state/UpState.h b/src/linkmgrd/src/link_state/UpState.h new file mode 100644 index 000000000000..7cbb867b2314 --- /dev/null +++ b/src/linkmgrd/src/link_state/UpState.h @@ -0,0 +1,106 @@ +/* + * UpState.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef LINK_STATE_UPSTATE_H_ +#define LINK_STATE_UPSTATE_H_ + +#include + +namespace link_state +{ + +/** + *@class DownState + * + *@brief maintains DownState state of LinkState + */ +class UpState: public LinkState +{ +public: + /** + *@method UpState + * + *@brief class default constructor + */ + UpState() = delete; + + /** + *@method UpState + * + *@brief class copy constructor + * + *@param UpState (in) reference to UpState object to be copied + */ + UpState(const UpState &) = delete; + + /** + *@method UpState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + UpState( + LinkStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~UpState + * + *@brief class destructor + */ + virtual ~UpState() = default; + + /** + *@method handleEvent + * + *@brief handle UpEvent from state db + * + *@param event (in) reference to UpEvent + * + *@return pointer to next LinkState + */ + virtual LinkState* handleEvent(UpEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle DownEvent from state db + * + *@param event (in) reference to DownEvent + * + *@return pointer to next LinkState + */ + virtual LinkState* handleEvent(DownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for LinkState label + * + *@return LinkState Up label + */ + virtual LinkState::Label getStateLabel() override {return LinkState::Label::Up;}; + +private: + uint8_t mDownEventCount = 0; +}; + +} /* namespace link_state */ + +#endif /* LINK_STATE_UPSTATE_H_ */ diff --git a/src/linkmgrd/src/link_state/subdir.mk b/src/linkmgrd/src/link_state/subdir.mk new file mode 100644 index 000000000000..f6883f6f781b --- /dev/null +++ b/src/linkmgrd/src/link_state/subdir.mk @@ -0,0 +1,29 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./src/link_state/DownState.cpp \ + ./src/link_state/LinkState.cpp \ + ./src/link_state/LinkStateMachine.cpp \ + ./src/link_state/UpState.cpp + +OBJS += \ + ./src/link_state/DownState.o \ + ./src/link_state/LinkState.o \ + ./src/link_state/LinkStateMachine.o \ + ./src/link_state/UpState.o + +CPP_DEPS += \ + ./src/link_state/DownState.d \ + ./src/link_state/LinkState.d \ + ./src/link_state/LinkStateMachine.d \ + ./src/link_state/UpState.d + + +# Each subdirectory must supply rules for building sources it contributes +src/link_state/%.o: src/link_state/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/src/linkmgrd/src/mux_state/ActiveState.cpp b/src/linkmgrd/src/mux_state/ActiveState.cpp new file mode 100644 index 000000000000..5bc853e2f2e3 --- /dev/null +++ b/src/linkmgrd/src/mux_state/ActiveState.cpp @@ -0,0 +1,108 @@ +/* + * ActiveState.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include "mux_state/ActiveState.h" +#include "mux_state/StandbyState.h" +#include "mux_state/MuxStateMachine.h" + +#include "common/MuxLogger.h" +#include "UnknownState.h" + +namespace mux_state +{ + +// +// ---> ActiveState(MuxStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +ActiveState::ActiveState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + MuxState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(ActiveEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* ActiveState::handleEvent(ActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState = + dynamic_cast (stateMachine->getActiveState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(StandbyEvent &event); +// +// handle StandbyEvent from state db/xcvrd +// +MuxState* ActiveState::handleEvent(StandbyEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState; + + mUnknownEventCount = 0; + if (++mStandbyEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + else { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + + return nextState; +} + +// +// ---> handleEvent(UnknownEvent &event); +// +// handle UnknownEvent from state db/xcvrd +// +MuxState* ActiveState::handleEvent(UnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState; + + mStandbyEventCount = 0; + if (++mUnknownEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUknownState()); + } + else { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void ActiveState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mStandbyEventCount = 0; + mUnknownEventCount = 0; +} + +} /* namespace mux_state */ diff --git a/src/linkmgrd/src/mux_state/ActiveState.h b/src/linkmgrd/src/mux_state/ActiveState.h new file mode 100644 index 000000000000..9f1a1f38099d --- /dev/null +++ b/src/linkmgrd/src/mux_state/ActiveState.h @@ -0,0 +1,118 @@ +/* + * ActiveState.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef MUX_STATE_ACTIVESTATE_H_ +#define MUX_STATE_ACTIVESTATE_H_ + +#include + +namespace mux_state +{ + +/** + *@class ActiveState + * + *@brief maintains ActiveState state of MuxState + */ +class ActiveState: public MuxState +{ +public: + /** + *@method ActiveState + * + *@brief class default constructor + */ + ActiveState() = delete; + + /** + *@method ActiveState + * + *@brief class copy constructor + * + *@param ActiveState (in) reference to ActiveState object to be copied + */ + ActiveState(const ActiveState &) = delete; + + /** + *@method DownState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + ActiveState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~ActiveState + * + *@brief class destructor + */ + virtual ~ActiveState() = default; + + /** + *@method handleEvent + * + *@brief handle ActiveEvent from state db/xcvrd + * + *@param event (in) reference to ActiveEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(ActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle StandbyEvent from state db/xcvrd + * + *@param event (in) reference to StandbyEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(StandbyEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle UnknownEvent from state db/xcvrd + * + *@param event (in) reference to UnknownEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(UnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for MuxState label + * + *@return MuxState Active label + */ + virtual MuxState::Label getStateLabel() override {return MuxState::Label::Active;}; + +private: + uint8_t mStandbyEventCount = 0; + uint8_t mUnknownEventCount = 0; +}; + +} /* namespace mux_state */ + +#endif /* MUX_STATE_ACTIVESTATE_H_ */ diff --git a/src/linkmgrd/src/mux_state/MuxState.cpp b/src/linkmgrd/src/mux_state/MuxState.cpp new file mode 100644 index 000000000000..e98213ff8aeb --- /dev/null +++ b/src/linkmgrd/src/mux_state/MuxState.cpp @@ -0,0 +1,30 @@ +/* + * MuxState.cpp + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#include +#include + +namespace mux_state +{ + +// +// ---> MuxState(MuxStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +MuxState::MuxState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +): + common::State( + *dynamic_cast (&stateMachine), + muxPortConfig + ) +{ +} + +} /* namespace mux_state */ diff --git a/src/linkmgrd/src/mux_state/MuxState.h b/src/linkmgrd/src/mux_state/MuxState.h new file mode 100644 index 000000000000..c70481c9c6e6 --- /dev/null +++ b/src/linkmgrd/src/mux_state/MuxState.h @@ -0,0 +1,124 @@ +/* + * MuxState.h + * + * Created on: Oct 18, 2020 + * Author: tamer + */ + +#ifndef MUX_STATE_MUXSTATE_H_ +#define MUX_STATE_MUXSTATE_H_ + +#include + +namespace mux_state +{ +class MuxStateMachine; +class ActiveEvent; +class StandbyEvent; +class UnknownEvent; + +/** + *@class MuxState + * + *@brief base class for different Mux states + */ +class MuxState: public common::State +{ +public: + /** + *@enum Label + * + *@brief Label corresponding to each MuxState State + */ + enum Label { + Active, + Standby, + Unknown, + Wait, + + Count + }; + +public: + /** + *@method MuxState + * + *@brief class default constructor + */ + MuxState() = delete; + + /** + *@method MuxState + * + *@brief class copy constructor + * + *@param MuxState (in) reference to MuxState object to be copied + */ + MuxState(const MuxState &) = delete; + + /** + *@method MuxState + * + *@brief class constructor + * + *@param stateMachine (in) reference to MuxStateMachine object + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + MuxState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~MuxState + * + *@brief class destructor + */ + virtual ~MuxState() = default; + + /** + *@method handleEvent + * + *@brief handle ActiveEvent from state db/xcvrd + * + *@param event (in) reference to ActiveEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(ActiveEvent &event) = 0; + + /** + *@method handleEvent + * + *@brief handle StandbyEvent from state db/xcvrd + * + *@param event (in) reference to StandbyEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(StandbyEvent &event) = 0; + + /** + *@method handleEvent + * + *@brief handle UnknownEvent from state db/xcvrd + * + *@param event (in) reference to UnknownEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(UnknownEvent &event) = 0; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual MuxState::Label getStateLabel() = 0; +}; + +} /* namespace mux_state */ + +#endif /* MUX_STATE_MUXSTATE_H_ */ diff --git a/src/linkmgrd/src/mux_state/MuxStateMachine.cpp b/src/linkmgrd/src/mux_state/MuxStateMachine.cpp new file mode 100644 index 000000000000..73eba57e8c0c --- /dev/null +++ b/src/linkmgrd/src/mux_state/MuxStateMachine.cpp @@ -0,0 +1,177 @@ +/* + * MuxStateMachine.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include + +#include "mux_state/MuxStateMachine.h" +#include "link_manager/LinkManagerStateMachine.h" +#include "common/MuxLogger.h" +#include "MuxState.h" + +namespace mux_state +{ +// +// static members +// +ActiveEvent MuxStateMachine::mActiveEvent; +StandbyEvent MuxStateMachine::mStandbyEvent; +UnknownEvent MuxStateMachine::mUnknownEvent; + +// +// ---> MuxStateMachine( +// link_manager::LinkManagerStateMachine &linkManagerStateMachine, +// boost::asio::io_service::strand &strand, +// common::MuxPortConfig &muxPortConfig, +// MuxState::Label label +// ); +// +// class constructor +// +MuxStateMachine::MuxStateMachine( + link_manager::LinkManagerStateMachine &linkManagerStateMachine, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig, + MuxState::Label label +) : + common::StateMachine(strand, muxPortConfig), + mLinkManagerStateMachine(linkManagerStateMachine), + mActiveState(*this, muxPortConfig), + mStandbyState(*this, muxPortConfig), + mUnknownState(*this, muxPortConfig), + mWaitState(*this, muxPortConfig) +{ + enterState(label); +} + +// +// ---> enterState(MuxState::Label label); +// +// force the state machine to enter a given state +// +void MuxStateMachine::enterState(MuxState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + switch (label) { + case MuxState::Label::Active: + setCurrentState(dynamic_cast (getActiveState())); + break; + case MuxState::Label::Standby: + setCurrentState(dynamic_cast (getStandbyState())); + break; + case MuxState::Label::Unknown: + setCurrentState(dynamic_cast (getUknownState())); + break; + case MuxState::Label::Wait: + setCurrentState(dynamic_cast (getWaitState())); + break; + default: + break; + } +} + +// +// ---> postLinkManagerEvent(MuxState* muxState); +// +// post MuxState change event to LinkManager state machine +// +inline +void MuxStateMachine::postLinkManagerEvent(MuxState* muxState) +{ + boost::asio::io_service::strand& strand = mLinkManagerStateMachine.getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&link_manager::LinkManagerStateMachine::handleStateChange), + &mLinkManagerStateMachine, + link_manager::LinkManagerStateMachine::getMuxStateEvent(), + muxState->getStateLabel() + ))); +} + +// +// ---> postMuxStateEvent(E &e); +// +// post MuxState event to the state machine +// +template +void MuxStateMachine::postMuxStateEvent(E &e) +{ + boost::asio::io_service::strand& strand = getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&MuxStateMachine::processEvent), + this, + e + ))); +} + +// +// ---> postMuxStateEvent(ActiveEvent &e); +// +// post MuxState event to the state machine +// +template +void MuxStateMachine::postMuxStateEvent(ActiveEvent &e); + +// +// ---> postMuxStateEvent(StandbyEvent &e); +// +// post MuxState event to the state machine +// +template +void MuxStateMachine::postMuxStateEvent(StandbyEvent &e); + +// +// ---> postMuxStateEvent(UnknownEvent &e); +// +// post MuxState event to the state machine +// +template +void MuxStateMachine::postMuxStateEvent(UnknownEvent &e); + +// +// ---> processEvent(T &t); +// +// process MuxState event +// +template +void MuxStateMachine::processEvent(T &t) +{ + MuxState *currentMuxState = dynamic_cast (getCurrentState()); + MuxState* nextMuxState = currentMuxState->handleEvent(t); + if (nextMuxState != currentMuxState) { + postLinkManagerEvent(nextMuxState); + } + setCurrentState(nextMuxState); +} + +// +// ---> processEvent(ActiveEvent &event); +// +// process MuxState event +// +template +void MuxStateMachine::processEvent(ActiveEvent &event); + +// +// ---> processEvent(StandbyEvent &event); +// +// process MuxState event +// +template +void MuxStateMachine::processEvent(StandbyEvent &event); + +// +// ---> processEvent(UnknownEvent &event); +// +// process MuxState event +// +template +void MuxStateMachine::processEvent(UnknownEvent &event); + +} /* namespace mux_state */ diff --git a/src/linkmgrd/src/mux_state/MuxStateMachine.h b/src/linkmgrd/src/mux_state/MuxStateMachine.h new file mode 100644 index 000000000000..4050f470b521 --- /dev/null +++ b/src/linkmgrd/src/mux_state/MuxStateMachine.h @@ -0,0 +1,249 @@ +/* + * MuxStateMachine.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef MUX_STATE_MUXSTATEMACHINE_H_ +#define MUX_STATE_MUXSTATEMACHINE_H_ + +#include "common/StateMachine.h" +#include "mux_state/ActiveState.h" +#include "mux_state/StandbyState.h" +#include "mux_state/WaitState.h" +#include "UnknownState.h" + +namespace link_manager { +class LinkManagerStateMachine; +} /* namespace link_manager */ + +namespace mux_state +{ +/** + *@class ActiveEvent + * + *@brief signals a ActiveEvent event to MuxState state machine + */ +class ActiveEvent { +public: + ActiveEvent() = default; + ~ActiveEvent() = default; +}; + +/** + *@class StandbyEvent + * + *@brief signals a StandbyEvent event to MuxState state machine + */ +class StandbyEvent { +public: + StandbyEvent() = default; + ~StandbyEvent() = default; +}; + +/** + *@class UnknownEvent + * + *@brief signals a UnknownEvent event to MuxState state machine + */ +class UnknownEvent { +public: + UnknownEvent() = default; + ~UnknownEvent() = default; +}; + +/** + *@class MuxStateMachine + * + *@brief maintains MuxState state machine + */ +class MuxStateMachine: public common::StateMachine +{ +public: + /** + *@method MuxStateMachine + * + *@brief class default constructor + */ + MuxStateMachine() = delete; + + /** + *@method MuxStateMachine + * + *@brief class copy constructor + * + *@param MuxStateMachine (in) reference to MuxStateMachine object to be copied + */ + MuxStateMachine(const MuxStateMachine &) = delete; + + /** + *@method MuxStateMachine + * + *@brief class constructor + * + *@param linkManagerStateMachine (in) reference to LinkManagerStateMachine + *@param strand (in) reference to boost serialization object + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param label (in) state machine initial state + */ + MuxStateMachine( + link_manager::LinkManagerStateMachine &linkManagerStateMachine, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig, + MuxState::Label label + ); + + /** + *@method ~MuxStateMachine + * + *@brief class destructor + */ + virtual ~MuxStateMachine() = default; + + /** + *@method enterState + * + *@brief force the state machine to enter a given state + * + *@param label (in) label of target state + * + *@return none + */ + void enterState(MuxState::Label label); + + /** + *@method postMuxStateEvent + * + *@brief post MuxState event to the state machine + * + *@param e (in) reference to the MuxState event + * + *@return none + */ + template + void postMuxStateEvent(E &e); + + /** + *@method processEvent + * + *@brief process MuxState event + * + *@param t (in) reference to the MuxState event + * + *@return none + */ + template + void processEvent(T &t); + + /** + *@method getActiveState + * + *@brief getter for ActiveState object + * + *@return pointer to ActiveState object + */ + ActiveState* getActiveState() {return &mActiveState;}; + + /** + *@method getStandbyState + * + *@brief getter for StandbyState object + * + *@return pointer to StandbyState object + */ + StandbyState* getStandbyState() {return &mStandbyState;}; + + /** + *@method getUnknownState + * + *@brief getter for UnknownState object + * + *@return pointer to UnknownState object + */ + UnknownState* getUknownState() {return &mUnknownState;}; + + /** + *@method getWaitState + * + *@brief getter for WaitState object + * + *@return pointer to WaitState object + */ + WaitState* getWaitState() {return &mWaitState;}; + + /** + *@method getActiveEvent + * + *@brief getter for ActiveEvent object + * + *@return pointer to ActiveEvent object + */ + static ActiveEvent& getActiveEvent() {return mActiveEvent;}; + + /** + *@method getStandbyEvent + * + *@brief getter for StandbyEvent object + * + *@return pointer to StandbyEvent object + */ + static StandbyEvent& getStandbyEvent() {return mStandbyEvent;}; + + /** + *@method getUnknownEvent + * + *@brief getter for UnknownEvent object + * + *@return pointer to UnknownEvent object + */ + static UnknownEvent& getUnknownEvent() {return mUnknownEvent;}; + + /** + *@method setWaitStateCause + * + *@brief setter Wait Cause + * + *@param waitStateCause (in) cause for entering wait state + * + *@return none + */ + void setWaitStateCause(WaitState::WaitStateCause waitStateCause) {mWaitState.setWaitStateCause(waitStateCause);}; + + /** + *@method getWaitOnSwssNotification + * + *@brief getter Wait Cause + * + *@return cause for entering wait state + */ + WaitState::WaitStateCause getWaitStateCause() const {return mWaitState.getWaitStateCause();}; + +private: + /** + *@method postLinkManagerEvent + * + *@brief post MuxState change event to LinkManager state machine + * + *@param muxState (in) pointer to current MuxState + * + *@return none + */ + inline void postLinkManagerEvent(MuxState* muxState); + +private: + static ActiveEvent mActiveEvent; + static StandbyEvent mStandbyEvent; + static UnknownEvent mUnknownEvent; + +private: + link_manager::LinkManagerStateMachine &mLinkManagerStateMachine; + ActiveState mActiveState; + StandbyState mStandbyState; + UnknownState mUnknownState; + WaitState mWaitState; +}; + +} /* namespace mux_state */ + +#endif /* MUX_STATE_MUXSTATEMACHINE_H_ */ diff --git a/src/linkmgrd/src/mux_state/StandbyState.cpp b/src/linkmgrd/src/mux_state/StandbyState.cpp new file mode 100644 index 000000000000..f39bae90d648 --- /dev/null +++ b/src/linkmgrd/src/mux_state/StandbyState.cpp @@ -0,0 +1,107 @@ +/* + * StandbyState.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include "mux_state/ActiveState.h" +#include "mux_state/StandbyState.h" +#include "mux_state/MuxStateMachine.h" + +#include "common/MuxLogger.h" +#include "UnknownState.h" + +namespace mux_state +{ + +// +// ---> StandbyState(MuxStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +StandbyState::StandbyState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + MuxState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(ActiveEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* StandbyState::handleEvent(ActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState; + + mUnknownEventCount = 0; + if (++mActiveEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + else { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + + return nextState; +} + +// +// ---> handleEvent(StandbyEvent &event); +// +// handle StandbyEvent from state db/xcvrd +// +MuxState* StandbyState::handleEvent(StandbyEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState = dynamic_cast (stateMachine->getStandbyState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(UnknownEvent &event); +// +// handle StandbyEvent from state db/xcvrd +// +MuxState* StandbyState::handleEvent(UnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState; + + mActiveEventCount = 0; + if (++mUnknownEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUknownState()); + } + else { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void StandbyState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mActiveEventCount = 0; + mUnknownEventCount = 0; +} + +} /* namespace mux_state */ diff --git a/src/linkmgrd/src/mux_state/StandbyState.h b/src/linkmgrd/src/mux_state/StandbyState.h new file mode 100644 index 000000000000..d3bee56faf96 --- /dev/null +++ b/src/linkmgrd/src/mux_state/StandbyState.h @@ -0,0 +1,118 @@ +/* + * StandbyState.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef MUX_STATE_STANDBYSTATE_H_ +#define MUX_STATE_STANDBYSTATE_H_ + +#include + +namespace mux_state +{ + +/** + *@class StandbyState + * + *@brief maintains StandbyState state of MuxState + */ +class StandbyState: public MuxState +{ +public: + /** + *@method StandbyState + * + *@brief class default constructor + */ + StandbyState() = delete; + + /** + *@method StandbyState + * + *@brief class copy constructor + * + *@param StandbyState (in) reference to StandbyState object to be copied + */ + StandbyState(const StandbyState &) = delete; + + /** + *@method StandbyState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + StandbyState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~StandbyState + * + *@brief class destructor + */ + virtual ~StandbyState() = default; + + /** + *@method handleEvent + * + *@brief handle ActiveEvent from state db/xcvrd + * + *@param event (in) reference to ActiveEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(ActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle StandbyEvent from state db/xcvrd + * + *@param event (in) reference to StandbyEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(StandbyEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle UnknownEvent from state db/xcvrd + * + *@param event (in) reference to UnknownEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(UnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for MuxState label + * + *@return MuxState Standby label + */ + virtual MuxState::Label getStateLabel() override {return MuxState::Label::Standby;}; + +private: + uint8_t mActiveEventCount = 0; + uint8_t mUnknownEventCount = 0; +}; + +} /* namespace mux_state */ + +#endif /* MUX_STATE_STANDBYSTATE_H_ */ diff --git a/src/linkmgrd/src/mux_state/UnknownState.cpp b/src/linkmgrd/src/mux_state/UnknownState.cpp new file mode 100644 index 000000000000..2e87f20db986 --- /dev/null +++ b/src/linkmgrd/src/mux_state/UnknownState.cpp @@ -0,0 +1,110 @@ +/* + * FailedState.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include "UnknownState.h" + +#include "mux_state/ActiveState.h" +#include "mux_state/StandbyState.h" +#include "mux_state/MuxStateMachine.h" + +#include "common/MuxLogger.h" + +namespace mux_state +{ + +// +// ---> UnknownState(MuxStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +UnknownState::UnknownState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + MuxState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(ActiveEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* UnknownState::handleEvent(ActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState; + + mStandbyEventCount = 0; + if (++mActiveEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + else { + nextState = dynamic_cast (stateMachine->getUknownState()); + } + + return nextState; +} + +// +// ---> handleEvent(StandbyEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* UnknownState::handleEvent(StandbyEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = dynamic_cast (getStateMachine()); + MuxState *nextState; + + mActiveEventCount = 0; + if (++mStandbyEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + else { + nextState = dynamic_cast (stateMachine->getUknownState()); + } + + return nextState; +} + +// +// ---> handleEvent(UnknownEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* UnknownState::handleEvent(UnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = + dynamic_cast (getStateMachine()); + MuxState *nextState = + dynamic_cast (stateMachine->getUknownState()); + + resetState(); + + return nextState; +} + +// +// ---> resetState() +// +// reset current state attributes +// +void UnknownState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mActiveEventCount = 0; + mStandbyEventCount = 0; +} + +} /* namespace mux_state */ diff --git a/src/linkmgrd/src/mux_state/UnknownState.h b/src/linkmgrd/src/mux_state/UnknownState.h new file mode 100644 index 000000000000..cb51fd096108 --- /dev/null +++ b/src/linkmgrd/src/mux_state/UnknownState.h @@ -0,0 +1,118 @@ +/* + * UnknownState.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef MUX_STATE_UNKNOWNSTATE_H_ +#define MUX_STATE_UNKNOWNSTATE_H_ + +#include + +namespace mux_state +{ + +/** + *@class UnknownState + * + *@brief maintains UnknownState state of MuxState + */ +class UnknownState: public MuxState +{ +public: + /** + *@method UnknownState + * + *@brief class default constructor + */ + UnknownState() = delete; + + /** + *@method UnknownState + * + *@brief class copy constructor + * + *@param UnknownState (in) reference to UnknownState object to be copied + */ + UnknownState(const UnknownState &) = delete; + + /** + *@method UnknownState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + UnknownState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~UnknownState + * + *@brief class destructor + */ + virtual ~UnknownState() = default; + + /** + *@method handleEvent + * + *@brief handle ActiveEvent from state db/xcvrd + * + *@param event (in) reference to ActiveEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(ActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle StandbyEvent from state db/xcvrd + * + *@param event (in) reference to StandbyEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(StandbyEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle UnknownEvent from state db/xcvrd + * + *@param event (in) reference to UnknownEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(UnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for MuxState label + * + *@return MuxState Unknown label + */ + virtual MuxState::Label getStateLabel() override {return MuxState::Label::Unknown;}; + +private: + uint8_t mActiveEventCount = 0; + uint8_t mStandbyEventCount = 0; +}; + +} /* namespace mux_state */ + +#endif /* MUX_STATE_UNKNOWNSTATE_H_ */ diff --git a/src/linkmgrd/src/mux_state/WaitState.cpp b/src/linkmgrd/src/mux_state/WaitState.cpp new file mode 100644 index 000000000000..261cbcb1b91d --- /dev/null +++ b/src/linkmgrd/src/mux_state/WaitState.cpp @@ -0,0 +1,120 @@ +/* + * WaitState.cpp + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#include "mux_state/ActiveState.h" +#include "mux_state/StandbyState.h" +#include "mux_state/MuxStateMachine.h" + +#include "common/MuxLogger.h" +#include "UnknownState.h" + +namespace mux_state +{ + +// +// ---> WaitState(MuxStateMachine &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +WaitState::WaitState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig +) : + MuxState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(ActiveEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* WaitState::handleEvent(ActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = + dynamic_cast (getStateMachine()); + MuxState *nextState; + + mFailedEventCount = 0; + mStandbyEventCount = 0; + if (++mActiveEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getActiveState()); + } + else { + nextState = dynamic_cast (stateMachine->getWaitState()); + } + + return nextState; +} + +// +// ---> handleEvent(StandbyEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* WaitState::handleEvent(StandbyEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = + dynamic_cast (getStateMachine()); + MuxState *nextState; + + mActiveEventCount = 0; + mFailedEventCount = 0; + if (++mStandbyEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getStandbyState()); + } + else { + nextState = dynamic_cast (stateMachine->getWaitState()); + } + + return nextState; +} + +// +// ---> handleEvent(UnknownEvent &event); +// +// handle ActiveEvent from state db/xcvrd +// +MuxState* WaitState::handleEvent(UnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + MuxStateMachine *stateMachine = + dynamic_cast (getStateMachine()); + MuxState *nextState; + + mActiveEventCount = 0; + mStandbyEventCount = 0; + if (++mFailedEventCount >= getMuxPortConfig().getMuxStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUknownState()); + } + else { + nextState = dynamic_cast (stateMachine->getWaitState()); + } + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void WaitState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mActiveEventCount = 0; + mStandbyEventCount = 0; + mFailedEventCount = 0; +} + +} /* namespace mux_state */ diff --git a/src/linkmgrd/src/mux_state/WaitState.h b/src/linkmgrd/src/mux_state/WaitState.h new file mode 100644 index 000000000000..cd366453d784 --- /dev/null +++ b/src/linkmgrd/src/mux_state/WaitState.h @@ -0,0 +1,153 @@ +/* + * WaitState.h + * + * Created on: Oct 20, 2020 + * Author: tamer + */ + +#ifndef MUX_STATE_WAITSTATE_H_ +#define MUX_STATE_WAITSTATE_H_ + +#include + +namespace mux_state +{ + +/** + *@class WaitState + * + *@brief maintains WaitState state of MuxState + */ +class WaitState: public MuxState +{ +public: + /** + *@enum WaitStateCause + * + *@brief WaitStateCause extends Wait state with cause attribute + */ + enum WaitStateCause { + CauseUnknown, + SwssUpdate, + DriverUpdate + }; + +public: + /** + *@method WaitState + * + *@brief class default constructor + */ + WaitState() = delete; + + /** + *@method WaitState + * + *@brief class copy constructor + * + *@param WaitState (in) reference to WaitState object to be copied + */ + WaitState(const WaitState &) = delete; + + /** + *@method WaitState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkStateMachine + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + WaitState( + MuxStateMachine &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~WaitState + * + *@brief class destructor + */ + virtual ~WaitState() = default; + + /** + *@method handleEvent + * + *@brief handle ActiveEvent from state db/xcvrd + * + *@param event (in) reference to ActiveEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(ActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle StandbyEvent from state db/xcvrd + * + *@param event (in) reference to StandbyEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(StandbyEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle UnknownEvent from state db/xcvrd + * + *@param event (in) reference to UnknownEvent + * + *@return pointer to next MuxState + */ + virtual MuxState* handleEvent(UnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for MuxState label + * + *@return MuxState Wait label + */ + virtual MuxState::Label getStateLabel() override {return MuxState::Label::Wait;}; + + /** + *@method setWaitStateCause + * + *@brief setter Wait Cause + * + *@param waitStateCause (in) cause for entering wait state + * + *@return none + */ + void setWaitStateCause(WaitStateCause waitStateCause) {mWaitStateCause = waitStateCause;}; + + /** + *@method getWaitOnSwssNotification + * + *@brief getter Wait Cause + * + *@return cause for entering wait state + */ + WaitStateCause getWaitStateCause() const {return mWaitStateCause;}; + +private: + uint8_t mActiveEventCount = 0; + uint8_t mStandbyEventCount = 0; + uint8_t mFailedEventCount = 0; + + WaitStateCause mWaitStateCause = CauseUnknown; +}; + +} /* namespace mux_state */ + +#endif /* MUX_STATE_WAITSTATE_H_ */ diff --git a/src/linkmgrd/src/mux_state/subdir.mk b/src/linkmgrd/src/mux_state/subdir.mk new file mode 100644 index 000000000000..46458f6727f4 --- /dev/null +++ b/src/linkmgrd/src/mux_state/subdir.mk @@ -0,0 +1,35 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./src/mux_state/ActiveState.cpp \ + ./src/mux_state/MuxState.cpp \ + ./src/mux_state/MuxStateMachine.cpp \ + ./src/mux_state/StandbyState.cpp \ + ./src/mux_state/UnknownState.cpp \ + ./src/mux_state/WaitState.cpp + +OBJS += \ + ./src/mux_state/ActiveState.o \ + ./src/mux_state/MuxState.o \ + ./src/mux_state/MuxStateMachine.o \ + ./src/mux_state/StandbyState.o \ + ./src/mux_state/UnknownState.o \ + ./src/mux_state/WaitState.o + +CPP_DEPS += \ + ./src/mux_state/ActiveState.d \ + ./src/mux_state/MuxState.d \ + ./src/mux_state/MuxStateMachine.d \ + ./src/mux_state/StandbyState.d \ + ./src/mux_state/UnknownState.d \ + ./src/mux_state/WaitState.d + + +# Each subdirectory must supply rules for building sources it contributes +src/mux_state/%.o: src/mux_state/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/src/linkmgrd/src/subdir.mk b/src/linkmgrd/src/subdir.mk new file mode 100644 index 000000000000..b9d836709f30 --- /dev/null +++ b/src/linkmgrd/src/subdir.mk @@ -0,0 +1,34 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./src/DbInterface.cpp \ + ./src/LinkMgrdMain.cpp \ + ./src/MuxManager.cpp \ + ./src/MuxPort.cpp \ + ./src/NetMsgInterface.cpp + +OBJS += \ + ./src/DbInterface.o \ + ./src/MuxManager.o \ + ./src/MuxPort.o \ + ./src/NetMsgInterface.o + +OBJS_LINKMGRD += \ + ./src/LinkMgrdMain.o \ + +CPP_DEPS += \ + ./src/DbInterface.d \ + ./src/LinkMgrdMain.d \ + ./src/MuxManager.d \ + ./src/MuxPort.d \ + ./src/NetMsgInterface.d + + +# Each subdirectory must supply rules for building sources it contributes +src/%.o: src/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/src/linkmgrd/test/FakeDbInterface.cpp b/src/linkmgrd/test/FakeDbInterface.cpp new file mode 100644 index 000000000000..83c0085d6bcd --- /dev/null +++ b/src/linkmgrd/test/FakeDbInterface.cpp @@ -0,0 +1,34 @@ +/* + * FakeDbInterface.cpp + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#include "FakeDbInterface.h" + +namespace test +{ + +FakeDbInterface::FakeDbInterface(boost::asio::io_service *ioService) : + mux::DbInterface(nullptr, ioService), + mNextMuxState(mux_state::MuxState::Label::Unknown) +{ +} + +void FakeDbInterface::setMuxState(const std::string &portName, mux_state::MuxState::Label label) +{ + mSetMuxStateInvokeCount++; +} + +void FakeDbInterface::getMuxState(const std::string &portName) +{ + mGetMuxStateInvokeCount++; +} + +void FakeDbInterface::probeMuxState(const std::string &portName) +{ + mProbeMuxStateInvokeCount++; +} + +} /* namespace test */ diff --git a/src/linkmgrd/test/FakeDbInterface.h b/src/linkmgrd/test/FakeDbInterface.h new file mode 100644 index 000000000000..6c2ff2f3438c --- /dev/null +++ b/src/linkmgrd/test/FakeDbInterface.h @@ -0,0 +1,40 @@ +/* + * FakeDbInterface.h + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#ifndef FAKEDBINTERFACE_H_ +#define FAKEDBINTERFACE_H_ + +#include "DbInterface.h" + +namespace test +{ + +class FakeDbInterface: public mux::DbInterface +{ +public: + FakeDbInterface(boost::asio::io_service *ioService); + virtual ~FakeDbInterface() = default; + + virtual void setMuxState(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; + + void setNextMuxState(mux_state::MuxState::Label label) {mNextMuxState = label;}; + +public: + mux_state::MuxState::Label mNextMuxState; + + uint32_t mMuxStateRequest[mux_state::MuxState::Label::Count] = {0, 0, 0, 0}; + + uint32_t mSetMuxStateInvokeCount = 0; + uint32_t mGetMuxStateInvokeCount = 0; + uint32_t mProbeMuxStateInvokeCount = 0; +}; + +} /* namespace test */ + +#endif /* FAKEDBINTERFACE_H_ */ diff --git a/src/linkmgrd/test/FakeLinkProber.cpp b/src/linkmgrd/test/FakeLinkProber.cpp new file mode 100644 index 000000000000..dbcb7764896b --- /dev/null +++ b/src/linkmgrd/test/FakeLinkProber.cpp @@ -0,0 +1,46 @@ +/* + * FakeLinkProber.cpp + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#include +#include + +#include "FakeLinkProber.h" + +namespace test +{ + +FakeLinkProber::FakeLinkProber( + link_prober::LinkProberStateMachine *linkProberStateMachine +) : + mLinkProberStateMachine(linkProberStateMachine) +{ +} + +template +void FakeLinkProber::postLinkProberEvent(E &e) +{ + boost::asio::io_service::strand& strand = mLinkProberStateMachine->getStrand(); + boost::asio::io_service &ios = strand.context(); + ios.post(strand.wrap(boost::bind( + static_cast + (&link_prober::LinkProberStateMachine::processEvent), + mLinkProberStateMachine, + e + ))); + +} + +template +void FakeLinkProber::postLinkProberEvent(link_prober::IcmpSelfEvent &event); + +template +void FakeLinkProber::postLinkProberEvent(link_prober::IcmpPeerEvent &event); + +template +void FakeLinkProber::postLinkProberEvent(link_prober::IcmpUnknownEvent &event); + +} /* namespace test */ diff --git a/src/linkmgrd/test/FakeLinkProber.h b/src/linkmgrd/test/FakeLinkProber.h new file mode 100644 index 000000000000..bcf8fe10a385 --- /dev/null +++ b/src/linkmgrd/test/FakeLinkProber.h @@ -0,0 +1,33 @@ +/* + * FakeLinkProber.h + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#ifndef FAKELINKPROBER_H_ +#define FAKELINKPROBER_H_ + +#include "link_prober/LinkProberStateMachine.h" +namespace test +{ + +class FakeLinkProber +{ +public: + FakeLinkProber(link_prober::LinkProberStateMachine *linkProberStateMachine); + virtual ~FakeLinkProber() = default; + + template + void postLinkProberEvent(E &e); + +public: + uint32_t mSuspendTxProbeCallCount = 0; + +private: + link_prober::LinkProberStateMachine *mLinkProberStateMachine; +}; + +} /* namespace test */ + +#endif /* FAKELINKPROBER_H_ */ diff --git a/src/linkmgrd/test/FakeMuxPort.cpp b/src/linkmgrd/test/FakeMuxPort.cpp new file mode 100644 index 000000000000..5fa3a652f0f2 --- /dev/null +++ b/src/linkmgrd/test/FakeMuxPort.cpp @@ -0,0 +1,46 @@ +/* + * MuxPort.cpp + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#include + +#include "FakeMuxPort.h" +#include "FakeLinkProber.h" + +namespace test +{ + +FakeMuxPort::FakeMuxPort( + FakeDbInterface *dbInterface, + common::MuxConfig &muxConfig, + std::string &portName, + uint16_t serverId, + boost::asio::io_service &ioService +) : + mux::MuxPort( + dbInterface, + muxConfig, + portName, + serverId, + ioService + ), + mFakeLinkProber( + std::make_shared(&getLinkManagerStateMachine()->getLinkProberStateMachine()) + ) +{ + setLinkProberPtr(std::dynamic_pointer_cast (mFakeLinkProber)); + link_manager::LinkManagerStateMachine::initializeTransitionFunctionTable(); +} + +void FakeMuxPort::activateStateMachine() +{ + setComponentInitState(0); + setComponentInitState(1); + setComponentInitState(2); + setComponentInitState(3); +} + +} /* namespace test */ diff --git a/src/linkmgrd/test/FakeMuxPort.h b/src/linkmgrd/test/FakeMuxPort.h new file mode 100644 index 000000000000..1d14f9d5e6b6 --- /dev/null +++ b/src/linkmgrd/test/FakeMuxPort.h @@ -0,0 +1,45 @@ +/* + * MuxPort.h + * + * Created on: Oct 23, 2020 + * Author: tamer + */ + +#ifndef FAKEMUXPORT_H_ +#define FAKEMUXPORT_H_ + +#include +#include + +#include "MuxPort.h" +#include "FakeDbInterface.h" +#include "FakeLinkProber.h" + +namespace test +{ + +class FakeMuxPort: public ::mux::MuxPort +{ +public: + FakeMuxPort( + FakeDbInterface *dbInterface, + common::MuxConfig &muxConfig, + std::string &portName, + uint16_t serverId, + boost::asio::io_service &ioService + ); + virtual ~FakeMuxPort() = default; + + void activateStateMachine(); + + const link_manager::LinkManagerStateMachine::CompositeState& getCompositeState() {return getLinkManagerStateMachine()->getCompositeState();}; + link_prober::LinkProberStateMachine& getLinkProberStateMachine() {return getLinkManagerStateMachine()->getLinkProberStateMachine();}; + mux_state::MuxStateMachine& getMuxStateMachine() {return getLinkManagerStateMachine()->getMuxStateMachine();}; + link_state::LinkStateMachine& getLinkStateMachine() {return getLinkManagerStateMachine()->getLinkStateMachine();}; + + std::shared_ptr mFakeLinkProber; +}; + +} /* namespace test */ + +#endif /* FAKEMUXPORT_H_ */ diff --git a/src/linkmgrd/test/LinkManagerStateMachineTest.cpp b/src/linkmgrd/test/LinkManagerStateMachineTest.cpp new file mode 100644 index 000000000000..ad251d3e86c2 --- /dev/null +++ b/src/linkmgrd/test/LinkManagerStateMachineTest.cpp @@ -0,0 +1,316 @@ +/* + * LinkManagerStateMachineTest.cpp + * + * Created on: Oct 25, 2020 + * Author: tamer + */ + +#include "LinkManagerStateMachineTest.h" +#include "link_prober/LinkProberStateMachine.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) + +namespace test +{ + +LinkManagerStateMachineTest::LinkManagerStateMachineTest() : + mDbInterface(&mIoService), + mFakeMuxPort( + &mDbInterface, + mMuxConfig, + mPortName, + mServerId, + mIoService + ) +{ +} + +void LinkManagerStateMachineTest::suspendTxProbes() +{ + mSuspendTxProbeCallCount++; +} + +void LinkManagerStateMachineTest::runIoService(uint32_t count) +{ + for (uint8_t i = 0; i < count; i++) { + mIoService.run(); + mIoService.reset(); + } +} + +void LinkManagerStateMachineTest::postLinkProberEvent(link_prober::LinkProberState::Label label) +{ + for (uint8_t i = 0; i < mMuxConfig.getStateChangeRetryCount(); i++) { + switch (label) { + case link_prober::LinkProberState::Active: + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachine::getIcmpSelfEvent() + ); + break; + case link_prober::LinkProberState::Standby: + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachine::getIcmpPeerEvent() + ); + break; + case link_prober::LinkProberState::Unknown: + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachine::getIcmpUnknownEvent() + ); + break; + default: + break; + } + runIoService(); + } +} + +void LinkManagerStateMachineTest::postMuxEvent(mux_state::MuxState::Label label) +{ + 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; + default: + break; + } + runIoService(); + } +} + +void LinkManagerStateMachineTest::postLinkEvent(link_state::LinkState::Label label) +{ + 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(); + } +} + +void LinkManagerStateMachineTest::handleMuxState(std::string state) +{ + mFakeMuxPort.handleMuxState(state); + runIoService(); +} + +void LinkManagerStateMachineTest::handleGetMuxState(std::string state) +{ + mFakeMuxPort.handleGetMuxState(state); + runIoService(); +} + +void LinkManagerStateMachineTest::handleProbeMuxState(std::string state) +{ + mFakeMuxPort.handleProbeMuxState(state); + runIoService(); +} + +void LinkManagerStateMachineTest::handleLinkState(std::string linkState) +{ + mFakeMuxPort.handleLinkState(linkState); + runIoService(); +} + +void LinkManagerStateMachineTest::handleMuxConfig(std::string config) +{ + mFakeMuxPort.handleMuxConfig(config); + runIoService(); +} + +void LinkManagerStateMachineTest::activateStateMachine() +{ + mFakeMuxPort.activateStateMachine(); +} + +void LinkManagerStateMachineTest::setMuxActive() +{ + activateStateMachine(); + VALIDATE_STATE(Unknown, Wait, Down); + + postLinkEvent(link_state::LinkState::Up); + VALIDATE_STATE(Unknown, Wait, Up); + + // change state to active + postLinkProberEvent(link_prober::LinkProberState::Active); + VALIDATE_STATE(Active, Wait, Up); + + // switch mux to active state + postMuxEvent(mux_state::MuxState::Active); + VALIDATE_STATE(Active, Active, Up); +} + +void LinkManagerStateMachineTest::setMuxStandby() +{ + activateStateMachine(); + VALIDATE_STATE(Unknown, Wait, Down); + + postLinkEvent(link_state::LinkState::Up); + VALIDATE_STATE(Unknown, Wait, Up); + + // change state to active + postLinkProberEvent(link_prober::LinkProberState::Standby); + VALIDATE_STATE(Standby, Wait, Up); + + // switch mux to active state + postMuxEvent(mux_state::MuxState::Standby); + VALIDATE_STATE(Standby, Standby, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxActiveSwitchOver) +{ + setMuxActive(); + + // verify MUX enters wait state and that the diver is being probed + EXPECT_EQ(mDbInterface.mProbeMuxStateInvokeCount, 0); + EXPECT_EQ(mDbInterface.mGetMuxStateInvokeCount, 1); + postLinkProberEvent(link_prober::LinkProberState::Standby); + VALIDATE_STATE(Standby, Wait, Up); + EXPECT_EQ(mDbInterface.mProbeMuxStateInvokeCount, 1); + + // fake mux statedb state to be active + mDbInterface.setNextMuxState(mux_state::MuxState::Active); + EXPECT_EQ(mDbInterface.mGetMuxStateInvokeCount, 1); + // driver notification + handleProbeMuxState("standby"); + VALIDATE_STATE(Standby, Standby, Up); + EXPECT_EQ(mDbInterface.mGetMuxStateInvokeCount, 2); + + // get state db mux state + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 0); + handleGetMuxState("active"); + VALIDATE_STATE(Standby, Wait, Up); + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 1); + + // swss notification + handleMuxState("standby"); + VALIDATE_STATE(Standby, Standby, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxStandbySwitchOver) +{ + setMuxStandby(); + + // verify MUX enters wait state and that the diver is being probed + EXPECT_EQ(mDbInterface.mProbeMuxStateInvokeCount, 0); + EXPECT_EQ(mDbInterface.mGetMuxStateInvokeCount, 1); + postLinkProberEvent(link_prober::LinkProberState::Active); + VALIDATE_STATE(Active, Wait, Up); + EXPECT_EQ(mDbInterface.mProbeMuxStateInvokeCount, 1); + + // fake mux statedb state to be standby + mDbInterface.setNextMuxState(mux_state::MuxState::Standby); + EXPECT_EQ(mDbInterface.mGetMuxStateInvokeCount, 1); + // driver notification + handleProbeMuxState("active"); + VALIDATE_STATE(Active, Active, Up); + EXPECT_EQ(mDbInterface.mGetMuxStateInvokeCount, 2); + + // get state db mux state + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 0); + handleGetMuxState("standby"); + VALIDATE_STATE(Active, Wait, Up); + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 1); + + // swss notification + handleMuxState("active"); + VALIDATE_STATE(Active, Active, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxActiveCliSwitchOver) +{ + setMuxActive(); + + handleMuxConfig("active"); + VALIDATE_STATE(Active, Active, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxStandbyCliSwitchOverMuxFirst) +{ + setMuxStandby(); + + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 0); + handleMuxConfig("active"); + + VALIDATE_STATE(Wait, Wait, Up); + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 1); + + // swss notification + handleMuxState("active"); + VALIDATE_STATE(Wait, Active, Up); + + // change state to active + postLinkProberEvent(link_prober::LinkProberState::Active); + VALIDATE_STATE(Active, Active, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxStandbyCliSwitchOverLinkProberFirst) +{ + setMuxStandby(); + + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 0); + handleMuxConfig("active"); + + VALIDATE_STATE(Wait, Wait, Up); + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 1); + + // change state to active + postLinkProberEvent(link_prober::LinkProberState::Active); + VALIDATE_STATE(Active, Wait, Up); + + // swss notification + handleMuxState("active"); + VALIDATE_STATE(Active, Active, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxActiveLinkDown) +{ + setMuxActive(); + + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 0); + handleLinkState("down"); + + VALIDATE_STATE(Active, Wait, Down); + EXPECT_EQ(mDbInterface.mSetMuxStateInvokeCount, 1); + + // swss notification + handleMuxState("standby"); + VALIDATE_STATE(Active, Standby, Down); + + handleLinkState("up"); + VALIDATE_STATE(Standby, Standby, Up); +} + +TEST_F(LinkManagerStateMachineTest, MuxStandbyLinkDown) +{ + setMuxStandby(); + + handleLinkState("down"); + VALIDATE_STATE(Standby, Standby, Down); + + handleLinkState("up"); + VALIDATE_STATE(Standby, Standby, Up); +} + +} /* namespace test */ diff --git a/src/linkmgrd/test/LinkManagerStateMachineTest.h b/src/linkmgrd/test/LinkManagerStateMachineTest.h new file mode 100644 index 000000000000..393b3834edab --- /dev/null +++ b/src/linkmgrd/test/LinkManagerStateMachineTest.h @@ -0,0 +1,56 @@ +/* + * LinkManagerStateMachineTest.h + * + * Created on: Oct 25, 2020 + * Author: tamer + */ + +#ifndef LINKMANAGERSTATEMACHINETEST_H_ +#define LINKMANAGERSTATEMACHINETEST_H_ + +#include "gtest/gtest.h" + +#include "FakeMuxPort.h" +#include "FakeLinkProber.h" + +namespace test +{ + +class LinkManagerStateMachineTest: public ::testing::Test +{ +public: + LinkManagerStateMachineTest(); + virtual ~LinkManagerStateMachineTest() = default; + + virtual void suspendTxProbes(); + + void runIoService(uint32_t count = 1); + void postLinkProberEvent(link_prober::LinkProberState::Label label); + void postMuxEvent(mux_state::MuxState::Label label); + void postLinkEvent(link_state::LinkState::Label label); + void handleMuxState(std::string); + void handleGetMuxState(std::string); + void handleProbeMuxState(std::string); + void handleLinkState(std::string linkState); + void handleMuxConfig(std::string config); + void activateStateMachine(); + void setMuxActive(); + void setMuxStandby(); + +public: + boost::asio::io_service mIoService; + common::MuxConfig mMuxConfig; + FakeDbInterface mDbInterface; + std::string mPortName = "EtherTest01"; + std::string mSmartNicIpAddress = "192.168.1.20"; + uint16_t mServerId = 01; + + FakeMuxPort mFakeMuxPort; + link_manager::LinkManagerStateMachine::CompositeState mTestCompositeState; + + uint32_t mSuspendTxProbeCallCount = 0; +}; + +} /* namespace test */ + +#endif /* LINKMANAGERSTATEMACHINETEST_H_ */ diff --git a/src/linkmgrd/test/subdir.mk b/src/linkmgrd/test/subdir.mk new file mode 100644 index 000000000000..b57d18789c3e --- /dev/null +++ b/src/linkmgrd/test/subdir.mk @@ -0,0 +1,29 @@ +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ + ./test/FakeDbInterface.cpp \ + ./test/FakeLinkProber.cpp \ + ./test/FakeMuxPort.cpp \ + ./test/LinkManagerStateMachineTest.cpp + +OBJS_LINKMGRD_TEST += \ + ./test/FakeDbInterface.o \ + ./test/FakeLinkProber.o \ + ./test/FakeMuxPort.o \ + ./test/LinkManagerStateMachineTest.o + +CPP_DEPS += \ + ./test/FakeDbInterface.d \ + ./test/FakeLinkProber.d \ + ./test/FakeMuxPort.d \ + ./test/LinkManagerStateMachineTest.d + + +# Each subdirectory must supply rules for building sources it contributes +test/%.o: test/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + g++ -std=c++17 -D__FILENAME__="$(subst src/,,$<)" -DBOOST_LOG_DYN_LINK $(INCLUDES) $(CPP_FLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + @echo 'Finished building: $<' + @echo ' ' + +