diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 3ba6fab5f9..ee313d3b5c 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -4,6 +4,7 @@ INCLUDES = -I $(top_srcdir)/lib \ -I switch \ -I flex_counter \ -I debug_counter \ + -I port \ -I pbh \ -I nhg @@ -58,6 +59,7 @@ orchagent_SOURCES = \ mplsrouteorch.cpp \ neighorch.cpp \ intfsorch.cpp \ + port/porthlpr.cpp \ portsorch.cpp \ fabricportsorch.cpp \ fgnhgorch.cpp \ diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 5bf01f9602..5647d48879 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -588,7 +588,7 @@ void MirrorOrch::setSessionState(const string& name, const MirrorEntry& session, Port port; if ((gMySwitchType == "voq") && (session.type == MIRROR_SESSION_ERSPAN)) { - if (!m_portsOrch->getRecircPort(port, "Rec")) + if (!m_portsOrch->getRecircPort(port, Port::Role::Rec)) { SWSS_LOG_ERROR("Failed to get recirc port for mirror session %s", name.c_str()); return; @@ -946,7 +946,7 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) if (gMySwitchType == "voq") { Port recirc_port; - if (!m_portsOrch->getRecircPort(recirc_port, "Rec")) + if (!m_portsOrch->getRecircPort(recirc_port, Port::Role::Rec)) { SWSS_LOG_ERROR("Failed to get recirc port"); return false; @@ -1177,7 +1177,7 @@ bool MirrorOrch::updateSessionDstPort(const string& name, MirrorEntry& session) // Set monitor port to recirc port in voq switch. if ((gMySwitchType == "voq") && (session.type == MIRROR_SESSION_ERSPAN)) { - if (!m_portsOrch->getRecircPort(port, "Rec")) + if (!m_portsOrch->getRecircPort(port, Port::Role::Rec)) { SWSS_LOG_ERROR("Failed to get recirc port for mirror session %s", name.c_str()); return false; diff --git a/orchagent/orch.h b/orchagent/orch.h index 3b72931d99..a594c24b41 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -9,8 +9,8 @@ #include extern "C" { -#include "sai.h" -#include "saistatus.h" +#include +#include } #include "dbconnector.h" diff --git a/orchagent/p4orch/tests/fake_portorch.cpp b/orchagent/p4orch/tests/fake_portorch.cpp index 69f62da78f..93b654c5e7 100644 --- a/orchagent/p4orch/tests/fake_portorch.cpp +++ b/orchagent/p4orch/tests/fake_portorch.cpp @@ -322,7 +322,7 @@ bool PortsOrch::setVoqInbandIntf(string &alias, string &type) return true; } -bool PortsOrch::getRecircPort(Port &p, string role) +bool PortsOrch::getRecircPort(Port &p, Port::Role role) { return true; } @@ -443,7 +443,7 @@ bool PortsOrch::setHostIntfsStripTag(Port &port, sai_hostif_vlan_tag_t strip) return true; } -bool PortsOrch::setBridgePortLearnMode(Port &port, string learn_mode) +bool PortsOrch::setBridgePortLearnMode(Port &port, sai_bridge_port_fdb_learning_mode_t learn_mode) { return true; } @@ -493,7 +493,7 @@ bool PortsOrch::setDistributionOnLagMember(Port &lagMember, bool enableDistribut return true; } -bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string fec) +bool PortsOrch::addPort(const PortConfig &port) { return true; } @@ -503,7 +503,7 @@ sai_status_t PortsOrch::removePort(sai_object_id_t port_id) return SAI_STATUS_SUCCESS; } -bool PortsOrch::initPort(const string &alias, const string &role, const int index, const set &lane_set) +bool PortsOrch::initPort(const PortConfig &port) { return true; } @@ -527,7 +527,7 @@ bool PortsOrch::setPortMtu(const Port &port, sai_uint32_t mtu) return true; } -bool PortsOrch::setPortTpid(sai_object_id_t id, sai_uint16_t tpid) +bool PortsOrch::setPortTpid(Port &port, sai_uint16_t tpid) { return true; } @@ -542,12 +542,12 @@ bool PortsOrch::getPortPvid(Port &port, sai_uint32_t &pvid) return true; } -bool PortsOrch::setPortFec(Port &port, std::string &mode) +bool PortsOrch::setPortFec(Port &port, sai_port_fec_mode_t fec_mode) { return true; } -bool PortsOrch::setPortPfcAsym(Port &port, string pfc_asym) +bool PortsOrch::setPortPfcAsym(Port &port, sai_port_priority_flow_control_mode_t pfc_asym) { return true; } @@ -596,7 +596,7 @@ bool PortsOrch::setGearboxPortAttr(const Port &port, dest_port_type_t port_type, return true; } -task_process_status PortsOrch::setPortAdvSpeeds(sai_object_id_t port_id, std::vector &speed_list) +task_process_status PortsOrch::setPortAdvSpeeds(Port &port, std::set &speed_list) { return task_success; } @@ -611,22 +611,17 @@ bool PortsOrch::isAutoNegEnabled(sai_object_id_t id) return true; } -task_process_status PortsOrch::setPortAutoNeg(sai_object_id_t id, int an) +task_process_status PortsOrch::setPortAutoNeg(Port &port, bool autoneg) { return task_success; } -bool PortsOrch::setPortFecMode(sai_object_id_t id, int fec) -{ - return true; -} - -task_process_status PortsOrch::setPortInterfaceType(sai_object_id_t id, sai_port_interface_type_t interface_type) +task_process_status PortsOrch::setPortInterfaceType(Port &port, sai_port_interface_type_t interface_type) { return task_success; } -task_process_status PortsOrch::setPortAdvInterfaceTypes(sai_object_id_t id, std::vector &interface_types) +task_process_status PortsOrch::setPortAdvInterfaceTypes(Port &port, std::set &interface_types) { return task_success; } @@ -648,21 +643,6 @@ void PortsOrch::getPortSerdesVal(const std::string &s, std::vector &la { } -bool PortsOrch::getPortAdvSpeedsVal(const std::string &s, std::vector &speed_values) -{ - return true; -} - -bool PortsOrch::getPortInterfaceTypeVal(const std::string &s, sai_port_interface_type_t &interface_type) -{ - return true; -} - -bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &s, std::vector &type_values) -{ - return true; -} - void PortsOrch::removePortSerdesAttribute(sai_object_id_t port_id) { } diff --git a/orchagent/port.h b/orchagent/port.h index 686b82d757..cf79267243 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -2,7 +2,7 @@ #define SWSS_PORT_H extern "C" { -#include "sai.h" +#include } #include @@ -12,6 +12,8 @@ extern "C" { #include #include +#include + #define DEFAULT_PORT_VLAN_ID 1 /* * Default MTU is derived from SAI_PORT_ATTR_MTU (1514) @@ -73,6 +75,9 @@ struct SystemLagInfo class Port { +public: + typedef sai_bridge_port_fdb_learning_mode_t port_learn_mode_t; + public: enum Type { CPU, @@ -85,14 +90,21 @@ class Port SUBPORT, SYSTEM, UNKNOWN - } ; + }; - enum AutoNegMode { - AUTONEG_NOT_SET = -1, - AUTONEG_OFF = 0, - AUTONEG_ON = 1 + enum Role + { + Ext, // external + Int, // internal + Inb, // inband + Rec // recirculation }; +public: + static constexpr std::size_t max_lanes = 8; // Max HW lanes + static constexpr std::size_t max_fec_modes = 3; // Max FEC modes (sync with SAI) + +public: Port() {}; Port(std::string alias, Type type) : m_alias(alias), m_type(type) {}; @@ -114,12 +126,12 @@ class Port std::string m_alias; Type m_type = UNKNOWN; - int m_index = 0; // PHY_PORT: index + uint16_t m_index = 0; // PHY_PORT: index uint32_t m_mtu = DEFAULT_MTU; uint32_t m_speed = 0; // Mbps - std::string m_learn_mode = "hardware"; - AutoNegMode m_autoneg = Port::AutoNegMode::AUTONEG_NOT_SET; - int m_link_training = -1; // -1 means not set, 0 = disabled, 1 = enabled + port_learn_mode_t m_learn_mode = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW; + bool m_autoneg = false; + bool m_link_training = false; bool m_admin_state_up = false; bool m_init = false; bool m_l3_vni = false; @@ -154,9 +166,9 @@ class Port uint32_t m_fdb_count = 0; uint32_t m_up_member_count = 0; uint32_t m_maximum_headroom = 0; - std::vector m_adv_speeds; + std::set m_adv_speeds; sai_port_interface_type_t m_interface_type = SAI_PORT_INTERFACE_TYPE_NONE; - std::vector m_adv_interface_types; + std::set m_adv_interface_types; bool m_mpls = false; /* * Following bit vector is used to lock @@ -181,8 +193,15 @@ class Port /* pre-emphasis */ std::map> m_preemphasis; - bool m_fec_cfg = false; - bool m_an_cfg = false; + /* Force initial parameter configuration flags */ + bool m_an_cfg = false; // Auto-negotiation (AN) + bool m_adv_speed_cfg = false; // Advertised speed + bool m_intf_cfg = false; // Interface type + bool m_adv_intf_cfg = false; // Advertised interface type + bool m_fec_cfg = false; // Forward Error Correction (FEC) + bool m_pfc_asym_cfg = false; // Asymmetric Priority Flow Control (PFC) + bool m_lm_cfg = false; // Forwarding Database (FDB) Learning Mode (LM) + bool m_lt_cfg = false; // Link Training (LT) int m_cap_an = -1; /* Capability - AutoNeg, -1 means not set */ int m_cap_lt = -1; /* Capability - LinkTraining, -1 means not set */ diff --git a/orchagent/port/portcnt.h b/orchagent/port/portcnt.h new file mode 100644 index 0000000000..c0c3ea359e --- /dev/null +++ b/orchagent/port/portcnt.h @@ -0,0 +1,178 @@ +#pragma once + +extern "C" { +#include +#include +} + +#include +#include + +#include +#include +#include +#include + +#include "../port.h" + +class PortConfig final +{ +public: + PortConfig() = default; + ~PortConfig() = default; + + PortConfig(const std::string &key, const std::string &op) noexcept + { + this->key = key; + this->op = op; + } + + struct { + std::string value; + bool is_set = false; + } alias; // Port alias + + struct { + std::uint16_t value; + bool is_set = false; + } index; // Interface index + + struct { + std::set value; + bool is_set = false; + } lanes; // Lane information of a physical port + + struct { + std::uint32_t value; + bool is_set = false; + } speed; // Port speed + + struct { + bool value; + bool is_set = false; + } autoneg; // Port autoneg + + struct { + std::set value; + bool is_set = false; + } adv_speeds; // Port advertised speeds + + struct { + sai_port_interface_type_t value; + bool is_set = false; + } interface_type; // Port interface type + + struct { + std::set value; + bool is_set = false; + } adv_interface_types; // Port advertised interface types + + struct { + sai_port_fec_mode_t value; + bool is_set = false; + } fec; // Port FEC + + struct { + std::uint32_t value; + bool is_set = false; + } mtu; // Port MTU + + struct { + std::uint16_t value; + bool is_set = false; + } tpid; // Port TPID + + struct { + sai_port_priority_flow_control_mode_t value; + bool is_set = false; + } pfc_asym; // Port asymmetric PFC + + struct { + sai_bridge_port_fdb_learning_mode_t value; + bool is_set = false; + } learn_mode; // Port FDB learn mode + + struct { + bool value; + bool is_set = false; + } link_training; // Port link training + + struct { + + struct { + std::vector value; + bool is_set = false; + } preemphasis; // Port serdes pre-emphasis + + struct { + std::vector value; + bool is_set = false; + } idriver; // Port serdes idriver + + struct { + std::vector value; + bool is_set = false; + } ipredriver; // Port serdes ipredriver + + struct { + std::vector value; + bool is_set = false; + } pre1; // Port serdes pre1 + + struct { + std::vector value; + bool is_set = false; + } pre2; // Port serdes pre2 + + struct { + std::vector value; + bool is_set = false; + } pre3; // Port serdes pre3 + + struct { + std::vector value; + bool is_set = false; + } main; // Port serdes main + + struct { + std::vector value; + bool is_set = false; + } post1; // Port serdes post1 + + struct { + std::vector value; + bool is_set = false; + } post2; // Port serdes post2 + + struct { + std::vector value; + bool is_set = false; + } post3; // Port serdes post3 + + struct { + std::vector value; + bool is_set = false; + } attn; // Port serdes attn + + } serdes; // Port serdes + + struct { + swss::Port::Role value; + bool is_set = false; + } role; // Port role + + struct { + bool value; + bool is_set = false; + } admin_status; // Port admin status + + struct { + std::string value; + bool is_set = false; + } description; // Port description + + std::string key; + std::string op; + + std::unordered_map fieldValueMap; +}; diff --git a/orchagent/port/porthlpr.cpp b/orchagent/port/porthlpr.cpp new file mode 100644 index 0000000000..95914c3e36 --- /dev/null +++ b/orchagent/port/porthlpr.cpp @@ -0,0 +1,947 @@ +// includes ----------------------------------------------------------------------------------------------------------- + +#include +#include + +#include +#include +#include + +#include + +#include "portschema.h" +#include "converter.h" +#include "tokenize.h" +#include "logger.h" + +#include "porthlpr.h" + +using namespace swss; + +// types -------------------------------------------------------------------------------------------------------------- + +typedef decltype(PortConfig::serdes) PortSerdes_t; + +// constants ---------------------------------------------------------------------------------------------------------- + +static const std::uint32_t minPortSpeed = 1; +static const std::uint32_t maxPortSpeed = 800000; + +static const std::uint32_t minPortMtu = 68; +static const std::uint32_t maxPortMtu = 9216; + +static const std::unordered_map portModeMap = +{ + { PORT_MODE_ON, true }, + { PORT_MODE_OFF, false } +}; + +static const std::unordered_map portStatusMap = +{ + { PORT_STATUS_UP, true }, + { PORT_STATUS_DOWN, false } +}; + +static const std::unordered_map portInterfaceTypeMap = +{ + { PORT_INTERFACE_TYPE_NONE, SAI_PORT_INTERFACE_TYPE_NONE }, + { PORT_INTERFACE_TYPE_CR, SAI_PORT_INTERFACE_TYPE_CR }, + { PORT_INTERFACE_TYPE_CR2, SAI_PORT_INTERFACE_TYPE_CR2 }, + { PORT_INTERFACE_TYPE_CR4, SAI_PORT_INTERFACE_TYPE_CR4 }, + { PORT_INTERFACE_TYPE_CR8, SAI_PORT_INTERFACE_TYPE_CR8 }, + { PORT_INTERFACE_TYPE_SR, SAI_PORT_INTERFACE_TYPE_SR }, + { PORT_INTERFACE_TYPE_SR2, SAI_PORT_INTERFACE_TYPE_SR2 }, + { PORT_INTERFACE_TYPE_SR4, SAI_PORT_INTERFACE_TYPE_SR4 }, + { PORT_INTERFACE_TYPE_SR8, SAI_PORT_INTERFACE_TYPE_SR8 }, + { PORT_INTERFACE_TYPE_LR, SAI_PORT_INTERFACE_TYPE_LR }, + { PORT_INTERFACE_TYPE_LR4, SAI_PORT_INTERFACE_TYPE_LR4 }, + { PORT_INTERFACE_TYPE_LR8, SAI_PORT_INTERFACE_TYPE_LR8 }, + { PORT_INTERFACE_TYPE_KR, SAI_PORT_INTERFACE_TYPE_KR }, + { PORT_INTERFACE_TYPE_KR4, SAI_PORT_INTERFACE_TYPE_KR4 }, + { PORT_INTERFACE_TYPE_KR8, SAI_PORT_INTERFACE_TYPE_KR8 }, + { PORT_INTERFACE_TYPE_CAUI, SAI_PORT_INTERFACE_TYPE_CAUI }, + { PORT_INTERFACE_TYPE_GMII, SAI_PORT_INTERFACE_TYPE_GMII }, + { PORT_INTERFACE_TYPE_SFI, SAI_PORT_INTERFACE_TYPE_SFI }, + { PORT_INTERFACE_TYPE_XLAUI, SAI_PORT_INTERFACE_TYPE_XLAUI }, + { PORT_INTERFACE_TYPE_KR2, SAI_PORT_INTERFACE_TYPE_KR2 }, + { PORT_INTERFACE_TYPE_CAUI4, SAI_PORT_INTERFACE_TYPE_CAUI4 }, + { PORT_INTERFACE_TYPE_XAUI, SAI_PORT_INTERFACE_TYPE_XAUI }, + { PORT_INTERFACE_TYPE_XFI, SAI_PORT_INTERFACE_TYPE_XFI }, + { PORT_INTERFACE_TYPE_XGMII, SAI_PORT_INTERFACE_TYPE_XGMII } +}; + +static const std::unordered_map portFecMap = +{ + { PORT_FEC_NONE, SAI_PORT_FEC_MODE_NONE }, + { PORT_FEC_RS, SAI_PORT_FEC_MODE_RS }, + { PORT_FEC_FC, SAI_PORT_FEC_MODE_FC } +}; + +static const std::unordered_map portFecRevMap = +{ + { SAI_PORT_FEC_MODE_NONE, PORT_FEC_NONE }, + { SAI_PORT_FEC_MODE_RS, PORT_FEC_RS }, + { SAI_PORT_FEC_MODE_FC, PORT_FEC_FC } +}; + +static const std::unordered_map portPfcAsymMap = +{ + { PORT_MODE_ON, SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE }, + { PORT_MODE_OFF, SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED } +}; + +static const std::unordered_map portLearnModeMap = +{ + { PORT_LEARN_MODE_DROP, SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DROP }, + { PORT_LEARN_MODE_DISABLE, SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE }, + { PORT_LEARN_MODE_HARDWARE, SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW }, + { PORT_LEARN_MODE_CPU_TRAP, SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_TRAP }, + { PORT_LEARN_MODE_CPU_LOG, SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_LOG }, + { PORT_LEARN_MODE_NOTIFICATION, SAI_BRIDGE_PORT_FDB_LEARNING_MODE_FDB_NOTIFICATION } +}; + +static const std::unordered_map portRoleMap = +{ + { PORT_ROLE_EXT, Port::Role::Ext }, + { PORT_ROLE_INT, Port::Role::Int }, + { PORT_ROLE_INB, Port::Role::Inb }, + { PORT_ROLE_REC, Port::Role::Rec } +}; + +// functions ---------------------------------------------------------------------------------------------------------- + +template +static inline T toUInt(const std::string &hexStr) +{ + if (hexStr.substr(0, 2) != "0x") + { + throw std::invalid_argument("Invalid argument: '" + hexStr + "'"); + } + + return to_uint(hexStr); +} + +static inline std::uint16_t toUInt16(const std::string &hexStr) +{ + return toUInt(hexStr); +} + +static inline std::uint32_t toUInt32(const std::string &hexStr) +{ + return toUInt(hexStr); +} + +// Port helper -------------------------------------------------------------------------------------------------------- + +bool PortHelper::fecToStr(std::string &str, sai_port_fec_mode_t value) const +{ + const auto &cit = portFecRevMap.find(value); + if (cit == portFecRevMap.cend()) + { + return false; + } + + str = cit->second; + + return true; +} + +std::string PortHelper::getFieldValueStr(const PortConfig &port, const std::string &field) const +{ + static std::string str; + + const auto &cit = port.fieldValueMap.find(field); + if (cit != port.fieldValueMap.cend()) + { + return cit->second; + } + + return str; +} + +std::string PortHelper::getAutonegStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_AUTONEG); +} + +std::string PortHelper::getPortInterfaceTypeStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_INTERFACE_TYPE); +} + +std::string PortHelper::getAdvInterfaceTypesStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_ADV_INTERFACE_TYPES); +} + +std::string PortHelper::getFecStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_FEC); +} + +std::string PortHelper::getPfcAsymStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_PFC_ASYM); +} + +std::string PortHelper::getLearnModeStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_LEARN_MODE); +} + +std::string PortHelper::getLinkTrainingStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_LINK_TRAINING); +} + +std::string PortHelper::getAdminStatusStr(const PortConfig &port) const +{ + return this->getFieldValueStr(port, PORT_ADMIN_STATUS); +} + +bool PortHelper::parsePortAlias(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty string is prohibited", field.c_str()); + return false; + } + + port.alias.value = value; + port.alias.is_set = true; + + return true; +} + +bool PortHelper::parsePortIndex(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + try + { + port.index.value = to_uint(value); + port.index.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + return true; +} + +bool PortHelper::parsePortLanes(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty string is prohibited", field.c_str()); + return false; + } + + const auto &laneList = tokenize(value, ','); + + try + { + for (const auto &cit : laneList) + { + port.lanes.value.insert(to_uint(cit)); + } + + port.lanes.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + if (port.lanes.value.size() != laneList.size()) + { + SWSS_LOG_WARN("Duplicate lanes in field(%s): unexpected value(%s)", field.c_str(), value.c_str()); + } + + return true; +} + +bool PortHelper::parsePortSpeed(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + try + { + port.speed.value = to_uint(value); + port.speed.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + if (!((minPortSpeed <= port.speed.value) && (port.speed.value <= maxPortSpeed))) + { + SWSS_LOG_ERROR( + "Failed to parse field(%s): value(%s) is out of range: %u <= speed <= %u", + field.c_str(), value.c_str(), minPortSpeed, maxPortSpeed + ); + return false; + } + + return true; +} + +bool PortHelper::parsePortAutoneg(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portModeMap.find(value); + if (cit == portModeMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.autoneg.value = cit->second; + port.autoneg.is_set = true; + + return true; +} + +bool PortHelper::parsePortAdvSpeeds(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + auto nValue = boost::algorithm::to_lower_copy(value); + + if (nValue == PORT_ADV_ALL) + { + port.adv_speeds.is_set = true; + return true; + } + + const auto &speedList = tokenize(nValue, ','); + + try + { + for (const auto &cit : speedList) + { + auto speed = to_uint(cit); + + if (!((minPortSpeed <= speed) && (speed <= maxPortSpeed))) + { + SWSS_LOG_ERROR( + "Failed to parse field(%s): value(%s) is out of range: %u <= speed <= %u", + field.c_str(), value.c_str(), minPortSpeed, maxPortSpeed + ); + return false; + } + + port.adv_speeds.value.insert(speed); + } + + port.adv_speeds.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + if (port.adv_speeds.value.size() != speedList.size()) + { + SWSS_LOG_WARN("Duplicate speeds in field(%s): unexpected value(%s)", field.c_str(), value.c_str()); + } + + return true; +} + +bool PortHelper::parsePortInterfaceType(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + auto nValue = boost::algorithm::to_lower_copy(value); + + const auto &cit = portInterfaceTypeMap.find(nValue); + if (cit == portInterfaceTypeMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.interface_type.value = cit->second; + port.interface_type.is_set = true; + + return true; +} + +bool PortHelper::parsePortAdvInterfaceTypes(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + auto nValue = boost::algorithm::to_lower_copy(value); + + if (nValue == PORT_ADV_ALL) + { + port.adv_interface_types.is_set = true; + return true; + } + + const auto &intfTypeList = tokenize(nValue, ','); + + for (const auto &cit1 : intfTypeList) + { + const auto &cit2 = portInterfaceTypeMap.find(cit1); + if (cit2 == portInterfaceTypeMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.adv_interface_types.value.insert(cit2->second); + } + + port.adv_interface_types.is_set = true; + + if (port.adv_interface_types.value.size() != intfTypeList.size()) + { + SWSS_LOG_WARN("Duplicate interface types in field(%s): unexpected value(%s)", field.c_str(), value.c_str()); + } + + return true; +} + +bool PortHelper::parsePortFec(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portFecMap.find(value); + if (cit == portFecMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.fec.value = cit->second; + port.fec.is_set = true; + + return true; +} + +bool PortHelper::parsePortMtu(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + try + { + port.mtu.value = to_uint(value); + port.mtu.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + if (!((minPortMtu <= port.mtu.value) && (port.mtu.value <= maxPortMtu))) + { + SWSS_LOG_ERROR( + "Failed to parse field(%s): value(%s) is out of range: %u <= mtu <= %u", + field.c_str(), value.c_str(), minPortMtu, maxPortMtu + ); + return false; + } + + return true; +} + +bool PortHelper::parsePortTpid(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + try + { + port.tpid.value = toUInt16(value); + port.tpid.is_set = true; + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + return true; +} + +bool PortHelper::parsePortPfcAsym(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portPfcAsymMap.find(value); + if (cit == portPfcAsymMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.pfc_asym.value = cit->second; + port.pfc_asym.is_set = true; + + return true; +} + +bool PortHelper::parsePortLearnMode(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portLearnModeMap.find(value); + if (cit == portLearnModeMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.learn_mode.value = cit->second; + port.learn_mode.is_set = true; + + return true; +} + +bool PortHelper::parsePortLinkTraining(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portModeMap.find(value); + if (cit == portModeMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.link_training.value = cit->second; + port.link_training.is_set = true; + + return true; +} + +template +bool PortHelper::parsePortSerdes(T &serdes, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty string is prohibited", field.c_str()); + return false; + } + + const auto &serdesList = tokenize(value, ','); + + try + { + for (const auto &cit : serdesList) + { + serdes.value.push_back(toUInt32(cit)); + } + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to parse field(%s): %s", field.c_str(), e.what()); + return false; + } + + serdes.is_set = true; + + return true; +} + +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::preemphasis) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::idriver) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::ipredriver) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::pre1) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::pre2) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::pre3) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::main) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::post1) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::post2) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::post3) &serdes, const std::string &field, const std::string &value) const; +template bool PortHelper::parsePortSerdes(decltype(PortSerdes_t::attn) &serdes, const std::string &field, const std::string &value) const; + +bool PortHelper::parsePortRole(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portRoleMap.find(value); + if (cit == portRoleMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.role.value = cit->second; + port.role.is_set = true; + + return true; +} + +bool PortHelper::parsePortAdminStatus(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + if (value.empty()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): empty value is prohibited", field.c_str()); + return false; + } + + const auto &cit = portStatusMap.find(value); + if (cit == portStatusMap.cend()) + { + SWSS_LOG_ERROR("Failed to parse field(%s): invalid value(%s)", field.c_str(), value.c_str()); + return false; + } + + port.admin_status.value = cit->second; + port.admin_status.is_set = true; + + return true; +} + +bool PortHelper::parsePortDescription(PortConfig &port, const std::string &field, const std::string &value) const +{ + SWSS_LOG_ENTER(); + + port.description.value = value; + port.description.is_set = true; + + return true; +} + +bool PortHelper::parsePortConfig(PortConfig &port) const +{ + SWSS_LOG_ENTER(); + + for (const auto &cit : port.fieldValueMap) + { + const auto &field = cit.first; + const auto &value = cit.second; + + if (field == PORT_ALIAS) + { + if (!this->parsePortAlias(port, field, value)) + { + return false; + } + } + else if (field == PORT_INDEX) + { + if (!this->parsePortIndex(port, field, value)) + { + return false; + } + } + else if (field == PORT_LANES) + { + if (!this->parsePortLanes(port, field, value)) + { + return false; + } + } + else if (field == PORT_SPEED) + { + if (!this->parsePortSpeed(port, field, value)) + { + return false; + } + } + else if (field == PORT_AUTONEG) + { + if (!this->parsePortAutoneg(port, field, value)) + { + return false; + } + } + else if (field == PORT_ADV_SPEEDS) + { + if (!this->parsePortAdvSpeeds(port, field, value)) + { + return false; + } + } + else if (field == PORT_INTERFACE_TYPE) + { + if (!this->parsePortInterfaceType(port, field, value)) + { + return false; + } + } + else if (field == PORT_ADV_INTERFACE_TYPES) + { + if (!this->parsePortAdvInterfaceTypes(port, field, value)) + { + return false; + } + } + else if (field == PORT_FEC) + { + if (!this->parsePortFec(port, field, value)) + { + return false; + } + } + else if (field == PORT_MTU) + { + if (!this->parsePortMtu(port, field, value)) + { + return false; + } + } + else if (field == PORT_TPID) + { + if (!this->parsePortTpid(port, field, value)) + { + return false; + } + } + else if (field == PORT_PFC_ASYM) + { + if (!this->parsePortPfcAsym(port, field, value)) + { + return false; + } + } + else if (field == PORT_LEARN_MODE) + { + if (!this->parsePortLearnMode(port, field, value)) + { + return false; + } + } + else if (field == PORT_LINK_TRAINING) + { + if (!this->parsePortLinkTraining(port, field, value)) + { + return false; + } + } + else if (field == PORT_PREEMPHASIS) + { + if (!this->parsePortSerdes(port.serdes.preemphasis, field, value)) + { + return false; + } + } + else if (field == PORT_IDRIVER) + { + if (!this->parsePortSerdes(port.serdes.idriver, field, value)) + { + return false; + } + } + else if (field == PORT_IPREDRIVER) + { + if (!this->parsePortSerdes(port.serdes.ipredriver, field, value)) + { + return false; + } + } + else if (field == PORT_PRE1) + { + if (!this->parsePortSerdes(port.serdes.pre1, field, value)) + { + return false; + } + } + else if (field == PORT_PRE2) + { + if (!this->parsePortSerdes(port.serdes.pre2, field, value)) + { + return false; + } + } + else if (field == PORT_PRE3) + { + if (!this->parsePortSerdes(port.serdes.pre3, field, value)) + { + return false; + } + } + else if (field == PORT_MAIN) + { + if (!this->parsePortSerdes(port.serdes.main, field, value)) + { + return false; + } + } + else if (field == PORT_POST1) + { + if (!this->parsePortSerdes(port.serdes.post1, field, value)) + { + return false; + } + } + else if (field == PORT_POST2) + { + if (!this->parsePortSerdes(port.serdes.post2, field, value)) + { + return false; + } + } + else if (field == PORT_POST3) + { + if (!this->parsePortSerdes(port.serdes.post3, field, value)) + { + return false; + } + } + else if (field == PORT_ATTN) + { + if (!this->parsePortSerdes(port.serdes.attn, field, value)) + { + return false; + } + } + else if (field == PORT_ROLE) + { + if (!this->parsePortRole(port, field, value)) + { + return false; + } + } + else if (field == PORT_ADMIN_STATUS) + { + if (!this->parsePortAdminStatus(port, field, value)) + { + return false; + } + } + else if (field == PORT_DESCRIPTION) + { + if (!this->parsePortDescription(port, field, value)) + { + return false; + } + } + else + { + SWSS_LOG_WARN("Unknown field(%s): skipping ...", field.c_str()); + } + } + + return this->validatePortConfig(port); +} + +bool PortHelper::validatePortConfig(PortConfig &port) const +{ + SWSS_LOG_ENTER(); + + if (!port.lanes.is_set) + { + SWSS_LOG_ERROR("Validation error: missing mandatory field(%s)", PORT_LANES); + return false; + } + + if (!port.speed.is_set) + { + SWSS_LOG_ERROR("Validation error: missing mandatory field(%s)", PORT_SPEED); + return false; + } + + if (!port.admin_status.is_set) + { + SWSS_LOG_INFO( + "Missing non mandatory field(%s): setting default value(%s)", + PORT_ADMIN_STATUS, + PORT_STATUS_DOWN + ); + + port.admin_status.value = false; + port.admin_status.is_set = true; + + port.fieldValueMap[PORT_ADMIN_STATUS] = PORT_STATUS_DOWN; + } + + return true; +} diff --git a/orchagent/port/porthlpr.h b/orchagent/port/porthlpr.h new file mode 100644 index 0000000000..f3a86e7054 --- /dev/null +++ b/orchagent/port/porthlpr.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include +#include + +#include "portcnt.h" + +class PortHelper final +{ +public: + PortHelper() = default; + ~PortHelper() = default; + +public: + bool fecToStr(std::string &str, sai_port_fec_mode_t value) const; + + std::string getAutonegStr(const PortConfig &port) const; + std::string getPortInterfaceTypeStr(const PortConfig &port) const; + std::string getAdvInterfaceTypesStr(const PortConfig &port) const; + std::string getFecStr(const PortConfig &port) const; + std::string getPfcAsymStr(const PortConfig &port) const; + std::string getLearnModeStr(const PortConfig &port) const; + std::string getLinkTrainingStr(const PortConfig &port) const; + std::string getAdminStatusStr(const PortConfig &port) const; + + bool parsePortConfig(PortConfig &port) const; + +private: + std::string getFieldValueStr(const PortConfig &port, const std::string &field) const; + + template + bool parsePortSerdes(T &serdes, const std::string &field, const std::string &value) const; + + bool parsePortAlias(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortIndex(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortLanes(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortSpeed(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortAutoneg(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortAdvSpeeds(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortInterfaceType(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortAdvInterfaceTypes(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortFec(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortMtu(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortTpid(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortPfcAsym(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortLearnMode(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortLinkTraining(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortRole(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortAdminStatus(PortConfig &port, const std::string &field, const std::string &value) const; + bool parsePortDescription(PortConfig &port, const std::string &field, const std::string &value) const; + + bool validatePortConfig(PortConfig &port) const; +}; diff --git a/orchagent/port/portschema.h b/orchagent/port/portschema.h new file mode 100644 index 0000000000..a01ea7271c --- /dev/null +++ b/orchagent/port/portschema.h @@ -0,0 +1,81 @@ +#pragma once + +// defines ------------------------------------------------------------------------------------------------------------ + +#define PORT_MODE_OFF "off" +#define PORT_MODE_ON "on" + +#define PORT_STATUS_DOWN "down" +#define PORT_STATUS_UP "up" + +#define PORT_ADV_ALL "all" + +#define PORT_INTERFACE_TYPE_NONE "none" +#define PORT_INTERFACE_TYPE_CR "cr" +#define PORT_INTERFACE_TYPE_CR2 "cr2" +#define PORT_INTERFACE_TYPE_CR4 "cr4" +#define PORT_INTERFACE_TYPE_CR8 "cr8" +#define PORT_INTERFACE_TYPE_SR "sr" +#define PORT_INTERFACE_TYPE_SR2 "sr2" +#define PORT_INTERFACE_TYPE_SR4 "sr4" +#define PORT_INTERFACE_TYPE_SR8 "sr8" +#define PORT_INTERFACE_TYPE_LR "lr" +#define PORT_INTERFACE_TYPE_LR4 "lr4" +#define PORT_INTERFACE_TYPE_LR8 "lr8" +#define PORT_INTERFACE_TYPE_KR "kr" +#define PORT_INTERFACE_TYPE_KR4 "kr4" +#define PORT_INTERFACE_TYPE_KR8 "kr8" +#define PORT_INTERFACE_TYPE_CAUI "caui" +#define PORT_INTERFACE_TYPE_GMII "gmii" +#define PORT_INTERFACE_TYPE_SFI "sfi" +#define PORT_INTERFACE_TYPE_XLAUI "xlaui" +#define PORT_INTERFACE_TYPE_KR2 "kr2" +#define PORT_INTERFACE_TYPE_CAUI4 "caui4" +#define PORT_INTERFACE_TYPE_XAUI "xaui" +#define PORT_INTERFACE_TYPE_XFI "xfi" +#define PORT_INTERFACE_TYPE_XGMII "xgmii" + +#define PORT_FEC_NONE "none" +#define PORT_FEC_RS "rs" +#define PORT_FEC_FC "fc" + +#define PORT_LEARN_MODE_DROP "drop" +#define PORT_LEARN_MODE_DISABLE "disable" +#define PORT_LEARN_MODE_HARDWARE "hardware" +#define PORT_LEARN_MODE_CPU_TRAP "cpu_trap" +#define PORT_LEARN_MODE_CPU_LOG "cpu_log" +#define PORT_LEARN_MODE_NOTIFICATION "notification" + +#define PORT_ROLE_EXT "Ext" +#define PORT_ROLE_INT "Int" +#define PORT_ROLE_INB "Inb" +#define PORT_ROLE_REC "Rec" + +#define PORT_ALIAS "alias" +#define PORT_INDEX "index" +#define PORT_LANES "lanes" +#define PORT_SPEED "speed" +#define PORT_AUTONEG "autoneg" +#define PORT_ADV_SPEEDS "adv_speeds" +#define PORT_INTERFACE_TYPE "interface_type" +#define PORT_ADV_INTERFACE_TYPES "adv_interface_types" +#define PORT_FEC "fec" +#define PORT_MTU "mtu" +#define PORT_TPID "tpid" +#define PORT_PFC_ASYM "pfc_asym" +#define PORT_LEARN_MODE "learn_mode" +#define PORT_LINK_TRAINING "link_training" +#define PORT_PREEMPHASIS "preemphasis" +#define PORT_IDRIVER "idriver" +#define PORT_IPREDRIVER "ipredriver" +#define PORT_PRE1 "pre1" +#define PORT_PRE2 "pre2" +#define PORT_PRE3 "pre3" +#define PORT_MAIN "main" +#define PORT_POST1 "post1" +#define PORT_POST2 "post2" +#define PORT_POST3 "post3" +#define PORT_ATTN "attn" +#define PORT_ROLE "role" +#define PORT_ADMIN_STATUS "admin_status" +#define PORT_DESCRIPTION "description" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 03bc0d74f3..c6919a20e8 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "net/if.h" @@ -59,6 +58,8 @@ extern string gMyHostName; extern string gMyAsicName; extern event_handle_t g_events_handle; +// defines ------------------------------------------------------------------------------------------------------------ + #define DEFAULT_SYSTEM_PORT_MTU 9100 #define VLAN_PREFIX "Vlan" #define DEFAULT_VLAN_ID 1 @@ -75,6 +76,17 @@ extern event_handle_t g_events_handle; #define PG_DROP_FLEX_STAT_COUNTER_POLL_MSECS "10000" #define PORT_RATE_FLEX_COUNTER_POLLING_INTERVAL_MS "1000" +// types -------------------------------------------------------------------------------------------------------------- + +struct PortAttrValue +{ + std::vector lanes; +}; + +typedef PortAttrValue PortAttrValue_t; +typedef std::map> PortSerdesAttrMap_t; + +// constants ---------------------------------------------------------------------------------------------------------- static map fec_mode_map = { @@ -90,12 +102,6 @@ static map fec_mode_reverse_map = { SAI_PORT_FEC_MODE_FC, "fc" } }; -static map pfc_asym_map = -{ - { "on", SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE }, - { "off", SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED } -}; - static map learn_mode_map = { { "drop", SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DROP }, @@ -125,12 +131,6 @@ static map autoneg_mode_map = { "off", 0 } }; -static map link_training_mode_map = -{ - { "on", 1 }, - { "off", 0 } -}; - static map link_training_failure_map = { { SAI_PORT_LINK_TRAINING_FAILURE_STATUS_NO_ERROR, "none" }, @@ -163,52 +163,6 @@ static map interface_type_map = { "kr8", SAI_PORT_INTERFACE_TYPE_KR8 } }; -// Interface type map used for auto negotiation -static map interface_type_map_for_an = -{ - { "none", SAI_PORT_INTERFACE_TYPE_NONE }, - { "cr", SAI_PORT_INTERFACE_TYPE_CR }, - { "cr2", SAI_PORT_INTERFACE_TYPE_CR2 }, - { "cr4", SAI_PORT_INTERFACE_TYPE_CR4 }, - { "cr8", SAI_PORT_INTERFACE_TYPE_CR8 }, - { "sr", SAI_PORT_INTERFACE_TYPE_SR }, - { "sr2", SAI_PORT_INTERFACE_TYPE_SR2 }, - { "sr4", SAI_PORT_INTERFACE_TYPE_SR4 }, - { "sr8", SAI_PORT_INTERFACE_TYPE_SR8 }, - { "lr", SAI_PORT_INTERFACE_TYPE_LR }, - { "lr4", SAI_PORT_INTERFACE_TYPE_LR4 }, - { "lr8", SAI_PORT_INTERFACE_TYPE_LR8 }, - { "kr", SAI_PORT_INTERFACE_TYPE_KR }, - { "kr4", SAI_PORT_INTERFACE_TYPE_KR4 }, - { "kr8", SAI_PORT_INTERFACE_TYPE_KR8 }, - { "caui", SAI_PORT_INTERFACE_TYPE_CAUI }, - { "gmii", SAI_PORT_INTERFACE_TYPE_GMII }, - { "sfi", SAI_PORT_INTERFACE_TYPE_SFI }, - { "xlaui", SAI_PORT_INTERFACE_TYPE_XLAUI }, - { "kr2", SAI_PORT_INTERFACE_TYPE_KR2 }, - { "caui4", SAI_PORT_INTERFACE_TYPE_CAUI4 }, - { "xaui", SAI_PORT_INTERFACE_TYPE_XAUI }, - { "xfi", SAI_PORT_INTERFACE_TYPE_XFI }, - { "xgmii", SAI_PORT_INTERFACE_TYPE_XGMII } -}; - -static const std::string& getValidInterfaceTypes() -{ - static std::string validInterfaceTypes; - if (validInterfaceTypes.empty()) - { - std::ostringstream oss; - for (auto &iter : interface_type_map_for_an) - { - oss << iter.first << " "; - } - validInterfaceTypes = oss.str(); - boost::to_upper(validInterfaceTypes); - } - - return validInterfaceTypes; -} - const vector port_stat_ids = { SAI_PORT_STAT_IF_IN_OCTETS, @@ -336,11 +290,73 @@ static char* hostif_vlan_tag[] = { [SAI_HOSTIF_VLAN_TAG_ORIGINAL] = "SAI_HOSTIF_VLAN_TAG_ORIGINAL" }; +// functions ---------------------------------------------------------------------------------------------------------- + static bool isValidPortTypeForLagMember(const Port& port) { return (port.m_type == Port::Type::PHY || port.m_type == Port::Type::SYSTEM); } +static void getPortSerdesAttr(PortSerdesAttrMap_t &map, const PortConfig &port) +{ + if (port.serdes.preemphasis.is_set) + { + map[SAI_PORT_SERDES_ATTR_PREEMPHASIS] = port.serdes.preemphasis.value; + } + + if (port.serdes.idriver.is_set) + { + map[SAI_PORT_SERDES_ATTR_IDRIVER] = port.serdes.idriver.value; + } + + if (port.serdes.ipredriver.is_set) + { + map[SAI_PORT_SERDES_ATTR_IPREDRIVER] = port.serdes.ipredriver.value; + } + + if (port.serdes.pre1.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_PRE1] = port.serdes.pre1.value; + } + + if (port.serdes.pre2.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_PRE2] = port.serdes.pre2.value; + } + + if (port.serdes.pre3.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_PRE3] = port.serdes.pre3.value; + } + + if (port.serdes.main.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_MAIN] = port.serdes.main.value; + } + + if (port.serdes.post1.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_POST1] = port.serdes.post1.value; + } + + if (port.serdes.post2.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_POST2] = port.serdes.post2.value; + } + + if (port.serdes.post3.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_POST3] = port.serdes.post3.value; + } + + if (port.serdes.attn.is_set) + { + map[SAI_PORT_SERDES_ATTR_TX_FIR_ATTN] = port.serdes.attn.value; + } +} + +// Port OA ------------------------------------------------------------------------------------------------------------ + /* * Initialize PortsOrch * 0) If Gearbox is enabled, then initialize the external PHYs as defined in @@ -451,100 +467,11 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vectorget_switch_attribute(gSwitchId, 1, &attr); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to get CPU port, rv:%d", status); - task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); - if (handle_status != task_process_status::task_success) - { - throw runtime_error("PortsOrch initialization failure"); - } - } - - m_cpuPort = Port("CPU", Port::CPU); - m_cpuPort.m_port_id = attr.value.oid; - m_portList[m_cpuPort.m_alias] = m_cpuPort; - m_port_ref_count[m_cpuPort.m_alias] = 0; - - /* Get port number */ - attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; - - status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to get port number, rv:%d", status); - task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); - if (handle_status != task_process_status::task_success) - { - throw runtime_error("PortsOrch initialization failure"); - } - } - - m_portCount = attr.value.u32; - SWSS_LOG_NOTICE("Get %d ports", m_portCount); - - /* Get port list */ - vector port_list; - port_list.resize(m_portCount); - - attr.id = SAI_SWITCH_ATTR_PORT_LIST; - attr.value.objlist.count = (uint32_t)port_list.size(); - attr.value.objlist.list = port_list.data(); + this->initializeCpuPort(); - status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to get port list, rv:%d", status); - task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); - if (handle_status != task_process_status::task_success) - { - throw runtime_error("PortsOrch initialization failure"); - } - } - - /* Get port hardware lane info */ - for (i = 0; i < m_portCount; i++) - { - sai_uint32_t lanes[8] = { 0,0,0,0,0,0,0,0 }; - attr.id = SAI_PORT_ATTR_HW_LANE_LIST; - attr.value.u32list.count = 8; - attr.value.u32list.list = lanes; - - status = sai_port_api->get_port_attribute(port_list[i], 1, &attr); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to get hardware lane list pid:%" PRIx64, port_list[i]); - task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); - if (handle_status != task_process_status::task_success) - { - throw runtime_error("PortsOrch initialization failure"); - } - } - - set tmp_lane_set; - for (j = 0; j < attr.value.u32list.count; j++) - { - tmp_lane_set.insert(attr.value.u32list.list[j]); - } - - string tmp_lane_str = ""; - for (auto s : tmp_lane_set) - { - tmp_lane_str += to_string(s) + " "; - } - tmp_lane_str = tmp_lane_str.substr(0, tmp_lane_str.size()-1); - - SWSS_LOG_NOTICE("Get port with lanes pid:%" PRIx64 " lanes:%s", port_list[i], tmp_lane_str.c_str()); - m_portListLaneMap[tmp_lane_set] = port_list[i]; - } + /* Get ports */ + this->initializePorts(); /* Get the flood control types and check if combined mode is supported */ vector supported_flood_control_types(max_flood_control_types, 0); @@ -586,6 +513,8 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector attrs; attr.id = SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID; attrs.push_back(attr); @@ -638,6 +567,312 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vectorget_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get CPU port, rv:%d", status); + auto handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch initialization failure"); + } + } + + this->m_cpuPort = Port("CPU", Port::CPU); + this->m_cpuPort.m_port_id = attr.value.oid; + this->m_portList[m_cpuPort.m_alias] = m_cpuPort; + this->m_port_ref_count[m_cpuPort.m_alias] = 0; + + SWSS_LOG_NOTICE("Get CPU port pid:%" PRIx64, this->m_cpuPort.m_port_id); +} + +void PortsOrch::initializePorts() +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + sai_attribute_t attr; + + // Get port number + attr.id = SAI_SWITCH_ATTR_PORT_NUMBER; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get port number, rv:%d", status); + auto handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch initialization failure"); + } + } + + this->m_portCount = attr.value.u32; + + SWSS_LOG_NOTICE("Get %d ports", this->m_portCount); + + // Get port list + std::vector portList(this->m_portCount, SAI_NULL_OBJECT_ID); + + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = static_cast(portList.size()); + attr.value.objlist.list = portList.data(); + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get port list, rv:%d", status); + auto handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch initialization failure"); + } + } + + // Get port hardware lane info + for (const auto &portId : portList) + { + std::vector laneList(Port::max_lanes, 0); + + attr.id = SAI_PORT_ATTR_HW_LANE_LIST; + attr.value.u32list.count = static_cast(laneList.size()); + attr.value.u32list.list = laneList.data(); + + status = sai_port_api->get_port_attribute(portId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get hardware lane list pid:%" PRIx64, portId); + auto handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch initialization failure"); + } + } + + std::set laneSet; + for (sai_uint32_t i = 0; i < attr.value.u32list.count; i++) + { + laneSet.insert(attr.value.u32list.list[i]); + } + + this->m_portListLaneMap[laneSet] = portId; + + SWSS_LOG_NOTICE( + "Get port with lanes pid:%" PRIx64 " lanes:%s", + portId, swss::join(" ", laneSet.cbegin(), laneSet.cend()).c_str() + ); + } +} + +auto PortsOrch::getPortConfigState() const -> port_config_state_t +{ + return this->m_portConfigState; +} + +void PortsOrch::setPortConfigState(port_config_state_t value) +{ + this->m_portConfigState = value; +} + +bool PortsOrch::addPortBulk(const std::vector &portList) +{ + // The method is used to create ports in a bulk mode. + // The action takes place when: + // 1. Ports are being initialized at system start + // 2. Ports are being added/removed by a user at runtime + + SWSS_LOG_ENTER(); + + if (portList.empty()) + { + return true; + } + + std::vector attrValueList; + std::vector> attrDataList; + std::vector attrCountList; + std::vector attrPtrList; + + auto portCount = static_cast(portList.size()); + std::vector oidList(portCount, SAI_NULL_OBJECT_ID); + std::vector statusList(portCount, SAI_STATUS_SUCCESS); + + for (const auto &cit : portList) + { + sai_attribute_t attr; + std::vector attrList; + + if (cit.lanes.is_set) + { + PortAttrValue_t attrValue; + auto &outList = attrValue.lanes; + auto &inList = cit.lanes.value; + outList.insert(outList.begin(), inList.begin(), inList.end()); + attrValueList.push_back(attrValue); + + attr.id = SAI_PORT_ATTR_HW_LANE_LIST; + attr.value.u32list.count = static_cast(attrValueList.back().lanes.size()); + attr.value.u32list.list = attrValueList.back().lanes.data(); + attrList.push_back(attr); + } + + if (cit.speed.is_set) + { + attr.id = SAI_PORT_ATTR_SPEED; + attr.value.u32 = cit.speed.value; + attrList.push_back(attr); + } + + if (cit.autoneg.is_set) + { + attr.id = SAI_PORT_ATTR_AUTO_NEG_MODE; + attr.value.booldata = cit.autoneg.value; + attrList.push_back(attr); + } + + if (cit.fec.is_set) + { + attr.id = SAI_PORT_ATTR_FEC_MODE; + attr.value.s32 = cit.fec.value; + attrList.push_back(attr); + } + + attrDataList.push_back(attrList); + attrCountList.push_back(static_cast(attrDataList.back().size())); + attrPtrList.push_back(attrDataList.back().data()); + } + + auto status = sai_port_api->create_ports( + gSwitchId, portCount, attrCountList.data(), attrPtrList.data(), + SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, + oidList.data(), statusList.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create ports with bulk operation, rv:%d", status); + + auto handle_status = handleSaiCreateStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch bulk create failure"); + } + + return false; + } + + for (std::uint32_t i = 0; i < portCount; i++) + { + if (statusList.at(i) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( + "Failed to create port %s with bulk operation, rv:%d", + portList.at(i).key.c_str(), statusList.at(i) + ); + + auto handle_status = handleSaiCreateStatus(SAI_API_PORT, statusList.at(i)); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch bulk create failure"); + } + + return false; + } + + m_portListLaneMap[portList.at(i).lanes.value] = oidList.at(i); + m_portCount++; + } + + // newly created ports might be put in the default vlan so remove all ports from + // the default vlan. + if (gMySwitchType == "voq") { + removeDefaultVlanMembers(); + removeDefaultBridgePorts(); + } + + SWSS_LOG_NOTICE("Created ports: %s", swss::join(',', oidList.begin(), oidList.end()).c_str()); + + return true; +} + +bool PortsOrch::removePortBulk(const std::vector &portList) +{ + SWSS_LOG_ENTER(); + + if (portList.empty()) + { + return true; + } + + for (const auto &cit : portList) + { + Port p; + + // Make sure to bring down admin state + if (getPort(cit, p)) + { + setPortAdminStatus(p, false); + } + // else : port is in default state or not yet created + + // Remove port serdes (if exists) before removing port since this reference is dependency + removePortSerdesAttribute(cit); + } + + auto portCount = static_cast(portList.size()); + std::vector statusList(portCount, SAI_STATUS_SUCCESS); + + auto status = sai_port_api->remove_ports( + portCount, portList.data(), + SAI_BULK_OP_ERROR_MODE_IGNORE_ERROR, + statusList.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove ports with bulk operation, rv:%d", status); + + auto handle_status = handleSaiRemoveStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch bulk remove failure"); + } + + return false; + } + + for (std::uint32_t i = 0; i < portCount; i++) + { + if (statusList.at(i) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR( + "Failed to remove port %" PRIx64 " with bulk operation, rv:%d", + portList.at(i), statusList.at(i) + ); + + auto handle_status = handleSaiRemoveStatus(SAI_API_PORT, statusList.at(i)); + if (handle_status != task_process_status::task_success) + { + SWSS_LOG_THROW("PortsOrch bulk remove failure"); + } + + return false; + } + + m_portSupportedSpeeds.erase(portList.at(i)); + m_portCount--; + } + + SWSS_LOG_NOTICE("Removed ports: %s", swss::join(',', portList.begin(), portList.end()).c_str()); + + return true; +} + void PortsOrch::removeDefaultVlanMembers() { /* Get VLAN members in default VLAN */ @@ -1242,59 +1477,43 @@ bool PortsOrch::setPortMtu(const Port& port, sai_uint32_t mtu) } -bool PortsOrch::setPortTpid(sai_object_id_t id, sai_uint16_t tpid) +bool PortsOrch::setPortTpid(Port &port, sai_uint16_t tpid) { SWSS_LOG_ENTER(); - sai_status_t status = SAI_STATUS_SUCCESS; - sai_attribute_t attr; + sai_attribute_t attr; attr.id = SAI_PORT_ATTR_TPID; + attr.value.u16 = tpid; - attr.value.u16 = (uint16_t)tpid; - - status = sai_port_api->set_port_attribute(id, &attr); + auto status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set TPID 0x%x to port pid:%" PRIx64 ", rv:%d", - attr.value.u16, id, status); + SWSS_LOG_ERROR("Failed to set TPID 0x%x to port %s, rv:%d", + attr.value.u16, port.m_alias.c_str(), status); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); if (handle_status != task_success) { return parseHandleSaiStatusFailure(handle_status); } } - else - { - SWSS_LOG_NOTICE("Set TPID 0x%x to port pid:%" PRIx64, attr.value.u16, id); - } + + SWSS_LOG_NOTICE("Set TPID 0x%x to port %s", attr.value.u16, port.m_alias.c_str()); + return true; } -bool PortsOrch::setPortFec(Port &port, string &mode) +bool PortsOrch::setPortFec(Port &port, sai_port_fec_mode_t fec_mode) { SWSS_LOG_ENTER(); - auto searchRef = m_portSupportedFecModes.find(port.m_port_id); - if (searchRef != m_portSupportedFecModes.end()) - { - auto &supportedFecModes = searchRef->second; - if (!supportedFecModes.empty() && (supportedFecModes.find(mode) == supportedFecModes.end())) - { - SWSS_LOG_ERROR("Unsupported mode %s on port %s", mode.c_str(), port.m_alias.c_str()); - // We return true becase the caller will keep the item in m_toSync and retry it later if we return false - // As the FEC mode is not supported it doesn't make sense to retry. - return true; - } - } - sai_attribute_t attr; attr.id = SAI_PORT_ATTR_FEC_MODE; - attr.value.s32 = port.m_fec_mode; + attr.value.s32 = fec_mode; sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set FEC mode %s to port %s", mode.c_str(), port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to set FEC mode %d to port %s", fec_mode, port.m_alias.c_str()); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); if (handle_status != task_success) { @@ -1302,9 +1521,9 @@ bool PortsOrch::setPortFec(Port &port, string &mode) } } - SWSS_LOG_NOTICE("Set port %s FEC mode %s", port.m_alias.c_str(), mode.c_str()); + setGearboxPortsAttr(port, SAI_PORT_ATTR_FEC_MODE, &fec_mode); - setGearboxPortsAttr(port, SAI_PORT_ATTR_FEC_MODE, &port.m_fec_mode); + SWSS_LOG_NOTICE("Set port %s FEC mode %d", port.m_alias.c_str(), fec_mode); return true; } @@ -1412,42 +1631,27 @@ bool PortsOrch::getPortPfcWatchdogStatus(sai_object_id_t portId, uint8_t *pfcwd_ return true; } -bool PortsOrch::setPortPfcAsym(Port &port, string pfc_asym) +bool PortsOrch::setPortPfcAsym(Port &port, sai_port_priority_flow_control_mode_t pfc_asym) { SWSS_LOG_ENTER(); - sai_attribute_t attr; uint8_t pfc = 0; - if (!getPortPfc(port.m_port_id, &pfc)) { return false; } - auto found = pfc_asym_map.find(pfc_asym); - if (found == pfc_asym_map.end()) - { - SWSS_LOG_ERROR("Incorrect asymmetric PFC mode: %s", pfc_asym.c_str()); - return false; - } - - auto new_pfc_asym = found->second; - if (port.m_pfc_asym == new_pfc_asym) - { - SWSS_LOG_NOTICE("Already set asymmetric PFC mode: %s", pfc_asym.c_str()); - return true; - } - - port.m_pfc_asym = new_pfc_asym; + port.m_pfc_asym = pfc_asym; m_portList[port.m_alias] = port; + sai_attribute_t attr; attr.id = SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_MODE; - attr.value.s32 = (int32_t) port.m_pfc_asym; + attr.value.s32 = pfc_asym; sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set PFC mode %d to port id 0x%" PRIx64 " (rc:%d)", port.m_pfc_asym, port.m_port_id, status); + SWSS_LOG_ERROR("Failed to set PFC mode %d to port id 0x%" PRIx64 " (rc:%d)", pfc_asym, port.m_port_id, status); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); if (handle_status != task_success) { @@ -1460,7 +1664,7 @@ bool PortsOrch::setPortPfcAsym(Port &port, string pfc_asym) return false; } - if (port.m_pfc_asym == SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE) + if (pfc_asym == SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE) { attr.id = SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_RX; attr.value.u8 = static_cast(0xff); @@ -1471,13 +1675,13 @@ bool PortsOrch::setPortPfcAsym(Port &port, string pfc_asym) SWSS_LOG_ERROR("Failed to set RX PFC 0x%x to port id 0x%" PRIx64 " (rc:%d)", attr.value.u8, port.m_port_id, status); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); if (handle_status != task_success) - { - return parseHandleSaiStatusFailure(handle_status); - } + { + return parseHandleSaiStatusFailure(handle_status); + } } } - SWSS_LOG_INFO("Set asymmetric PFC %s to port id 0x%" PRIx64, pfc_asym.c_str(), port.m_port_id); + SWSS_LOG_INFO("Set asymmetric PFC %d to port id 0x%" PRIx64, pfc_asym, port.m_port_id); return true; } @@ -2109,64 +2313,81 @@ void PortsOrch::initPortCapLinkTraining(Port &port) SWSS_LOG_WARN("Unable to get %s LT support capability", port.m_alias.c_str()); } -void PortsOrch::getPortSupportedFecModes(const std::string& alias, sai_object_id_t port_id, PortSupportedFecModes &supported_fecmodes) +bool PortsOrch::isFecModeSupported(const Port &port, sai_port_fec_mode_t fec_mode) { - sai_attribute_t attr; - sai_status_t status; - vector fecModes(fec_mode_reverse_map.size()); + initPortSupportedFecModes(port.m_alias, port.m_port_id); + + const auto &obj = m_portSupportedFecModes.at(port.m_port_id); + + if (!obj.supported) + { + return true; + } + + if (obj.data.empty()) + { + return false; + } + + return std::find(obj.data.cbegin(), obj.data.cend(), fec_mode) != obj.data.cend(); +} + +sai_status_t PortsOrch::getPortSupportedFecModes(PortSupportedFecModes &supported_fecmodes, sai_object_id_t port_id) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + std::vector fecModes(Port::max_fec_modes); attr.id = SAI_PORT_ATTR_SUPPORTED_FEC_MODE; attr.value.s32list.count = static_cast(fecModes.size()); attr.value.s32list.list = fecModes.data(); - status = sai_port_api->get_port_attribute(port_id, 1, &attr); - fecModes.resize(attr.value.s32list.count); + auto status = sai_port_api->get_port_attribute(port_id, 1, &attr); if (status == SAI_STATUS_SUCCESS) { - if (fecModes.empty()) - { - supported_fecmodes.insert("N/A"); - } - else + for (std::uint32_t i = 0; i < attr.value.s32list.count; i++) { - for(auto fecMode : fecModes) - { - supported_fecmodes.insert(fec_mode_reverse_map[static_cast(fecMode)]); - } + supported_fecmodes.insert(static_cast(attr.value.s32list.list[i])); } } else { if (SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status) || - status == SAI_STATUS_NOT_IMPLEMENTED) + (status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED)) { // unable to validate FEC mode if attribute is not supported on platform - SWSS_LOG_NOTICE("Unable to validate FEC mode for port %s id=%" PRIx64 " due to unsupported by platform", - alias.c_str(), port_id); + SWSS_LOG_NOTICE( + "Unable to validate FEC mode for port id=%" PRIx64 " due to unsupported by platform", port_id + ); } else { - SWSS_LOG_ERROR("Failed to get a list of supported FEC modes for port %s id=%" PRIx64 ". Error=%d", - alias.c_str(), port_id, status); + SWSS_LOG_ERROR( + "Failed to get a list of supported FEC modes for port id=%" PRIx64 ". Error=%d", port_id, status + ); } - - supported_fecmodes.clear(); // return empty } + + return status; } void PortsOrch::initPortSupportedFecModes(const std::string& alias, sai_object_id_t port_id) { + SWSS_LOG_ENTER(); + // If port supported speeds map already contains the information, save the SAI call - if (m_portSupportedFecModes.count(port_id)) + if (m_portSupportedFecModes.count(port_id) > 0) { return; } - PortSupportedFecModes supported_fec_modes; - getPortSupportedFecModes(alias, port_id, supported_fec_modes); - m_portSupportedFecModes[port_id] = supported_fec_modes; - if (supported_fec_modes.empty()) + auto &obj = m_portSupportedFecModes[port_id]; + auto &supported_fec_modes = obj.data; + + auto status = getPortSupportedFecModes(supported_fec_modes, port_id); + if (status != SAI_STATUS_SUCCESS) { // Do not expose "supported_fecs" in case fetching FEC modes is not supported by the vendor SWSS_LOG_INFO("No supported_fecs exposed to STATE_DB for port %s since fetching supported FEC modes is not supported by the vendor", @@ -2174,19 +2395,35 @@ void PortsOrch::initPortSupportedFecModes(const std::string& alias, sai_object_i return; } - vector v; - std::string supported_fec_modes_str; - bool first = true; - for(auto fec : supported_fec_modes) + obj.supported = true; + + std::vector fecModeList; + if (supported_fec_modes.empty()) { - if (first) - first = false; - else - supported_fec_modes_str += ','; - supported_fec_modes_str += fec; + fecModeList.push_back("N/A"); } + else + { + for (const auto &cit : supported_fec_modes) + { + std::string fecMode; + if (!m_portHlpr.fecToStr(fecMode, cit)) + { + SWSS_LOG_ERROR( + "Failed to convert FEC mode for port %s: unknown value %d", + alias.c_str(), static_cast(cit) + ); + continue; + } + fecModeList.push_back(fecMode); + } + } + + std::vector v; + std::string supported_fec_modes_str = swss::join(',', fecModeList.begin(), fecModeList.end()); v.emplace_back(std::make_pair("supported_fecs", supported_fec_modes_str)); + m_portStateTable.set(alias, v); } @@ -2392,17 +2629,17 @@ bool PortsOrch::getPortAdvSpeeds(const Port& port, bool remote, string& adv_spee return rc; } -task_process_status PortsOrch::setPortAdvSpeeds(sai_object_id_t port_id, std::vector& speed_list) +task_process_status PortsOrch::setPortAdvSpeeds(Port &port, std::set &speed_list) { SWSS_LOG_ENTER(); - sai_attribute_t attr; - sai_status_t status; + sai_attribute_t attr; + std::vector speedList(speed_list.begin(), speed_list.end()); attr.id = SAI_PORT_ATTR_ADVERTISED_SPEED; - attr.value.u32list.list = speed_list.data(); - attr.value.u32list.count = static_cast(speed_list.size()); + attr.value.u32list.list = speedList.data(); + attr.value.u32list.count = static_cast(speedList.size()); - status = sai_port_api->set_port_attribute(port_id, &attr); + auto status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { return handleSaiSetStatus(SAI_API_PORT, status); @@ -2411,16 +2648,15 @@ task_process_status PortsOrch::setPortAdvSpeeds(sai_object_id_t port_id, std::ve return task_success; } -task_process_status PortsOrch::setPortInterfaceType(sai_object_id_t port_id, sai_port_interface_type_t interface_type) +task_process_status PortsOrch::setPortInterfaceType(Port &port, sai_port_interface_type_t interface_type) { SWSS_LOG_ENTER(); - sai_attribute_t attr; - sai_status_t status; + sai_attribute_t attr; attr.id = SAI_PORT_ATTR_INTERFACE_TYPE; - attr.value.u32 = static_cast(interface_type); + attr.value.s32 = interface_type; - status = sai_port_api->set_port_attribute(port_id, &attr); + auto status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { return handleSaiSetStatus(SAI_API_PORT, status); @@ -2429,17 +2665,17 @@ task_process_status PortsOrch::setPortInterfaceType(sai_object_id_t port_id, sai return task_success; } -task_process_status PortsOrch::setPortAdvInterfaceTypes(sai_object_id_t port_id, std::vector &interface_types) +task_process_status PortsOrch::setPortAdvInterfaceTypes(Port &port, std::set &interface_types) { SWSS_LOG_ENTER(); - sai_attribute_t attr; - sai_status_t status; + sai_attribute_t attr; + std::vector interfaceTypeList(interface_types.begin(), interface_types.end()); attr.id = SAI_PORT_ATTR_ADVERTISED_INTERFACE_TYPE; - attr.value.u32list.list = interface_types.data(); - attr.value.u32list.count = static_cast(interface_types.size()); + attr.value.s32list.list = interfaceTypeList.data(); + attr.value.s32list.count = static_cast(interfaceTypeList.size()); - status = sai_port_api->set_port_attribute(port_id, &attr); + auto status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { return handleSaiSetStatus(SAI_API_PORT, status); @@ -2525,21 +2761,21 @@ bool PortsOrch::isAutoNegEnabled(sai_object_id_t id) return attr.value.booldata; } -task_process_status PortsOrch::setPortAutoNeg(sai_object_id_t id, int an) +task_process_status PortsOrch::setPortAutoNeg(Port &port, bool autoneg) { SWSS_LOG_ENTER(); sai_attribute_t attr; attr.id = SAI_PORT_ATTR_AUTO_NEG_MODE; - attr.value.booldata = (an == 1 ? true : false); + attr.value.booldata = autoneg; - sai_status_t status = sai_port_api->set_port_attribute(id, &attr); + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set AutoNeg %u to port pid:%" PRIx64, attr.value.booldata, id); + SWSS_LOG_ERROR("Failed to set AutoNeg %u to port %s", attr.value.booldata, port.m_alias.c_str()); return handleSaiSetStatus(SAI_API_PORT, status); } - SWSS_LOG_INFO("Set AutoNeg %u to port pid:%" PRIx64, attr.value.booldata, id); + SWSS_LOG_INFO("Set AutoNeg %u to port %s", attr.value.booldata, port.m_alias.c_str()); return task_success; } @@ -2681,11 +2917,11 @@ void PortsOrch::updateDbPortOperStatus(const Port& port, sai_port_oper_status_t m_portTable->set(port.m_alias, tuples); } -bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string fec_mode) +bool PortsOrch::addPort(const PortConfig &port) { SWSS_LOG_ENTER(); - if (!speed || lane_set.empty()) + if (!port.speed.is_set || !port.lanes.is_set) { /* speed and lane list are mandatory attributes for the initial create_port call @@ -2694,13 +2930,13 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string return true; } - vector lanes(lane_set.begin(), lane_set.end()); + vector lanes(port.lanes.value.begin(), port.lanes.value.end()); sai_attribute_t attr; vector attrs; attr.id = SAI_PORT_ATTR_SPEED; - attr.value.u32 = speed; + attr.value.u32 = port.speed.value; attrs.push_back(attr); attr.id = SAI_PORT_ATTR_HW_LANE_LIST; @@ -2708,17 +2944,17 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string attr.value.u32list.count = static_cast(lanes.size()); attrs.push_back(attr); - if (an == true) + if (port.autoneg.is_set) { attr.id = SAI_PORT_ATTR_AUTO_NEG_MODE; - attr.value.booldata = true; + attr.value.booldata = port.autoneg.value; attrs.push_back(attr); } - if (!fec_mode.empty()) + if (port.fec.is_set) { attr.id = SAI_PORT_ATTR_FEC_MODE; - attr.value.u32 = fec_mode_map[fec_mode]; + attr.value.s32 = port.fec.value; attrs.push_back(attr); } @@ -2726,7 +2962,7 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string sai_status_t status = sai_port_api->create_port(&port_id, gSwitchId, static_cast(attrs.size()), attrs.data()); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to create port with the speed %u, rv:%d", speed, status); + SWSS_LOG_ERROR("Failed to create port with the speed %u, rv:%d", port.speed.value, status); task_process_status handle_status = handleSaiCreateStatus(SAI_API_PORT, status); if (handle_status != task_success) { @@ -2734,7 +2970,7 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string } } - m_portListLaneMap[lane_set] = port_id; + m_portListLaneMap[port.lanes.value] = port_id; m_portCount++; // newly created ports might be put in the default vlan so remove all ports from @@ -2744,7 +2980,7 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string removeDefaultBridgePorts(); } - SWSS_LOG_NOTICE("Create port %" PRIx64 " with the speed %u", port_id, speed); + SWSS_LOG_NOTICE("Create port %" PRIx64 " with the speed %u", port_id, port.speed.value); return true; } @@ -2810,10 +3046,15 @@ string PortsOrch::getPriorityGroupDropPacketsFlexCounterTableKey(string key) return string(PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; } -bool PortsOrch::initPort(const string &alias, const string &role, const int index, const set &lane_set) +bool PortsOrch::initPort(const PortConfig &port) { SWSS_LOG_ENTER(); + const auto &alias = port.key; + const auto &role = port.role.value; + const auto &index = port.index.value; + const auto &lane_set = port.lanes.value; + /* Determine if the lane combination exists in switch */ if (m_portListLaneMap.find(lane_set) != m_portListLaneMap.end()) { @@ -2877,7 +3118,7 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde m_portList[alias].m_init = true; - if (role == "Rec" || role == "Inb") + if (role == Port::Role::Rec || role == Port::Role::Inb) { m_recircPortRole[alias] = role; } @@ -3002,10 +3243,9 @@ void PortsOrch::cleanPortTable(const vector& keys) void PortsOrch::removePortFromLanesMap(string alias) { - for (auto it = m_lanesAliasSpeedMap.begin(); it != m_lanesAliasSpeedMap.end(); it++) { - if (get<0>(it->second) == alias) + if (it->second.key == alias) { SWSS_LOG_NOTICE("Removing port %s from lanes map", alias.c_str()); it = m_lanesAliasSpeedMap.erase(it); @@ -3033,248 +3273,111 @@ void PortsOrch::doPortTask(Consumer &consumer) { SWSS_LOG_ENTER(); - auto it = consumer.m_toSync.begin(); - while (it != consumer.m_toSync.end()) + auto &taskMap = consumer.m_toSync; + auto it = taskMap.begin(); + + while (it != taskMap.end()) { - auto &t = it->second; + auto keyOpFieldsValues = it->second; + auto key = kfvKey(keyOpFieldsValues); + auto op = kfvOp(keyOpFieldsValues); - string alias = kfvKey(t); - string op = kfvOp(t); + SWSS_LOG_INFO("KEY: %s, OP: %s", key.c_str(), op.c_str()); - if (alias == "PortConfigDone") + if (key.empty()) { - if (m_portConfigState != PORT_CONFIG_MISSING) - { - // Already received, ignore this task - it = consumer.m_toSync.erase(it); - continue; - } - - m_portConfigState = PORT_CONFIG_RECEIVED; - - for (auto i : kfvFieldsValues(t)) - { - if (fvField(i) == "count") - { - m_portCount = to_uint(fvValue(i)); - } - } + SWSS_LOG_ERROR("Failed to parse port key: empty string"); + it = taskMap.erase(it); + continue; } - /* Get notification from application */ - /* portsyncd application: - * When portsorch receives 'PortInitDone' message, it indicates port initialization - * procedure is done. Before port initialization procedure, none of other tasks - * are executed. + /* Got notification from portsyncd application: + * + * When portsorch receives 'PortConfigDone' message, it indicates port configuration + * procedure is done. Port configuration assumes all data has been read from config db + * and pushed to application db. + * + * Before port configuration procedure, none of other tasks are executed. */ - if (alias == "PortInitDone") + if (key == "PortConfigDone") { + it = taskMap.erase(it); + /* portsyncd restarting case: * When portsyncd restarts, duplicate notifications may be received. */ - if (!m_initDone) - { - addSystemPorts(); - m_initDone = true; - SWSS_LOG_INFO("Get PortInitDone notification from portsyncd."); - } - - it = consumer.m_toSync.erase(it); - return; - - } - - if (op == SET_COMMAND) - { - set lane_set; - vector attr_val; - map> serdes_attr; - typedef pair> serdes_attr_pair; - string admin_status; - string fec_mode; - string pfc_asym; - uint32_t mtu = 0; - uint32_t speed = 0; - string learn_mode; - string an_str; - string lt_str; - int an = -1; - int lt = -1; - int index = -1; - string role; - string adv_speeds_str; - string interface_type_str; - string adv_interface_types_str; - vector adv_speeds; - sai_port_interface_type_t interface_type; - vector adv_interface_types; - string tpid_string; - uint16_t tpid = 0; - - for (auto i : kfvFieldsValues(t)) - { - attr_val.clear(); - /* Set interface index */ - if (fvField(i) == "index") - { - index = (int)stoul(fvValue(i)); - } - /* Get lane information of a physical port and initialize the port */ - else if (fvField(i) == "lanes") - { - string lane_str; - istringstream iss(fvValue(i)); - - while (getline(iss, lane_str, ',')) - { - int lane = stoi(lane_str); - lane_set.insert(lane); - } - } - /* Set port admin status */ - else if (fvField(i) == "admin_status") - { - admin_status = fvValue(i); - } - /* Set port MTU */ - else if (fvField(i) == "mtu") - { - mtu = (uint32_t)stoul(fvValue(i)); - } - /* Set port TPID */ - if (fvField(i) == "tpid") - { - tpid_string = fvValue(i); - // Need to get rid of the leading 0x - tpid_string.erase(0,2); - tpid = (uint16_t)stoi(tpid_string, 0, 16); - SWSS_LOG_DEBUG("Handling TPID to 0x%x, string value:%s", tpid, tpid_string.c_str()); - } - /* Set port speed */ - else if (fvField(i) == "speed") - { - speed = (uint32_t)stoul(fvValue(i)); - } - /* Set port fec */ - else if (fvField(i) == "fec") - { - fec_mode = fvValue(i); - } - /* Get port fdb learn mode*/ - else if (fvField(i) == "learn_mode") - { - learn_mode = fvValue(i); - } - /* Set port asymmetric PFC */ - else if (fvField(i) == "pfc_asym") - { - pfc_asym = fvValue(i); - } - /* Set autoneg and ignore the port speed setting */ - else if (fvField(i) == "autoneg") - { - an_str = fvValue(i); - } - /* Set advertised speeds */ - else if (fvField(i) == "adv_speeds") - { - adv_speeds_str = fvValue(i); - } - /* Set interface type */ - else if (fvField(i) == "interface_type") - { - interface_type_str = fvValue(i); - } - /* Set advertised interface type */ - else if (fvField(i) == "adv_interface_types") - { - adv_interface_types_str = fvValue(i); - } - /* Set link training */ - else if (fvField(i) == "link_training") - { - lt_str = fvValue(i); - } - /* Set port serdes Pre-emphasis */ - else if (fvField(i) == "preemphasis") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_PREEMPHASIS, attr_val)); - } - /* Set port serdes idriver */ - else if (fvField(i) == "idriver") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_IDRIVER, attr_val)); - } - /* Set port serdes ipredriver */ - else if (fvField(i) == "ipredriver") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_IPREDRIVER, attr_val)); - } - /* Set port serdes pre1 */ - else if (fvField(i) == "pre1") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_PRE1, attr_val)); - } - /* Set port serdes pre2 */ - else if (fvField(i) == "pre2") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_PRE2, attr_val)); - } - /* Set port serdes pre3 */ - else if (fvField(i) == "pre3") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_PRE3, attr_val)); - } - /* Set port serdes main */ - else if (fvField(i) == "main") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_MAIN, attr_val)); - } - /* Set port serdes post1 */ - else if (fvField(i) == "post1") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_POST1, attr_val)); - } - /* Set port serdes post2 */ - else if (fvField(i) == "post2") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_POST2, attr_val)); - } - /* Set port serdes post3 */ - else if (fvField(i) == "post3") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_POST3, attr_val)); - } - /* Set port serdes attn */ - else if (fvField(i) == "attn") - { - getPortSerdesVal(fvValue(i), attr_val); - serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_ATTN, attr_val)); - } + if (getPortConfigState() != PORT_CONFIG_MISSING) + { + // Already received, ignore this task + continue; + } - /* Get port role */ - if (fvField(i) == "role") + setPortConfigState(PORT_CONFIG_RECEIVED); + + for (const auto &cit : kfvFieldsValues(keyOpFieldsValues)) + { + if (fvField(cit) == "count") { - role = fvValue(i); + m_portCount = to_uint(fvValue(cit)); } } - /* Collect information about all received ports */ - if (lane_set.size()) + SWSS_LOG_INFO("Got PortConfigDone notification from portsyncd"); + + it = taskMap.begin(); + continue; + } + + /* Got notification from portsyncd application: + * + * When portsorch receives 'PortInitDone' message, it indicates port initialization + * procedure is done. Port initialization assumes all netdevs have been created. + * + * Before port initialization procedure, none of other tasks are executed. + */ + if (key == "PortInitDone") + { + /* portsyncd restarting case: + * When portsyncd restarts, duplicate notifications may be received. + */ + if (!m_initDone) + { + addSystemPorts(); + m_initDone = true; + SWSS_LOG_INFO("Got PortInitDone notification from portsyncd"); + } + + it = taskMap.erase(it); + continue; + } + + PortConfig pCfg(key, op); + + if (op == SET_COMMAND) + { + auto &fvMap = m_portConfigMap[key]; + + for (const auto &cit : kfvFieldsValues(keyOpFieldsValues)) + { + auto fieldName = fvField(cit); + auto fieldValue = fvValue(cit); + + SWSS_LOG_INFO("FIELD: %s, VALUE: %s", fieldName.c_str(), fieldValue.c_str()); + + fvMap[fieldName] = fieldValue; + } + + pCfg.fieldValueMap = fvMap; + + if (!m_portHlpr.parsePortConfig(pCfg)) { - m_lanesAliasSpeedMap[lane_set] = make_tuple(alias, speed, an, fec_mode, index, role); + it = taskMap.erase(it); + continue; } + /* Collect information about all received ports */ + m_lanesAliasSpeedMap[pCfg.lanes.value] = pCfg; + // TODO: // Fix the issue below // After PortConfigDone, while waiting for "PortInitDone" and the first gBufferOrch->isPortReady(alias), @@ -3286,108 +3389,123 @@ void PortsOrch::doPortTask(Consumer &consumer) * 2. Create new ports * 3. Initialize all ports */ - if (m_portConfigState == PORT_CONFIG_RECEIVED || m_portConfigState == PORT_CONFIG_DONE) + if (getPortConfigState() != PORT_CONFIG_MISSING) { + std::vector portsToAddList; + std::vector portsToRemoveList; + + // Port remove comparison logic for (auto it = m_portListLaneMap.begin(); it != m_portListLaneMap.end();) { if (m_lanesAliasSpeedMap.find(it->first) == m_lanesAliasSpeedMap.end()) { - if (SAI_STATUS_SUCCESS != removePort(it->second)) - { - throw runtime_error("PortsOrch initialization failure."); - } + portsToRemoveList.push_back(it->second); it = m_portListLaneMap.erase(it); + continue; } - else + + it++; + } + + // Bulk port remove + if (!portsToRemoveList.empty()) + { + if (!removePortBulk(portsToRemoveList)) { - it++; + SWSS_LOG_THROW("PortsOrch initialization failure"); } } + // Port add comparison logic for (auto it = m_lanesAliasSpeedMap.begin(); it != m_lanesAliasSpeedMap.end();) { if (m_portListLaneMap.find(it->first) == m_portListLaneMap.end()) { - if (!addPort(it->first, get<1>(it->second), get<2>(it->second), get<3>(it->second))) - { - throw runtime_error("PortsOrch initialization failure."); - } + portsToAddList.push_back(it->second); + it++; + continue; } - if (!initPort(get<0>(it->second), get<5>(it->second), get<4>(it->second), it->first)) + if (!initPort(it->second)) { // Failure has been recorded in initPort it++; continue; } - initPortSupportedSpeeds(get<0>(it->second), m_portListLaneMap[it->first]); - initPortSupportedFecModes(get<0>(it->second), m_portListLaneMap[it->first]); + initPortSupportedSpeeds(it->second.key, m_portListLaneMap[it->first]); + initPortSupportedFecModes(it->second.key, m_portListLaneMap[it->first]); + it++; } - m_portConfigState = PORT_CONFIG_DONE; + // Bulk port add + if (!portsToAddList.empty()) + { + if (!addPortBulk(portsToAddList)) + { + SWSS_LOG_THROW("PortsOrch initialization failure"); + } + + for (const auto &cit : portsToAddList) + { + if (!initPort(cit)) + { + // Failure has been recorded in initPort + continue; + } + + initPortSupportedSpeeds(cit.key, m_portListLaneMap[cit.lanes.value]); + initPortSupportedFecModes(cit.key, m_portListLaneMap[cit.lanes.value]); + } + } + + setPortConfigState(PORT_CONFIG_DONE); } - if (m_portConfigState != PORT_CONFIG_DONE) + if (getPortConfigState() != PORT_CONFIG_DONE) { // Not yet receive PortConfigDone. Save it for future retry it++; continue; } - if (alias == "PortConfigDone") - { - it = consumer.m_toSync.erase(it); - continue; - } - - if (!gBufferOrch->isPortReady(alias)) + if (!gBufferOrch->isPortReady(pCfg.key)) { // buffer configuration hasn't been applied yet. save it for future retry - m_pendingPortSet.emplace(alias); + m_pendingPortSet.emplace(pCfg.key); it++; continue; } else { - m_pendingPortSet.erase(alias); + m_pendingPortSet.erase(pCfg.key); } Port p; - if (!getPort(alias, p)) + if (!getPort(pCfg.key, p)) { - SWSS_LOG_ERROR("Failed to get port id by alias:%s", alias.c_str()); + SWSS_LOG_ERROR("Failed to get port id by alias: %s", pCfg.key.c_str()); } else { - if (admin_status.empty()) - { - admin_status = p.m_admin_state_up ? "up" : "down"; - } + PortSerdesAttrMap_t serdes_attr; + getPortSerdesAttr(serdes_attr, pCfg); - if (!an_str.empty()) + if (pCfg.autoneg.is_set) { - if (autoneg_mode_map.find(an_str) == autoneg_mode_map.end()) - { - SWSS_LOG_ERROR("Failed to parse autoneg value: %s", an_str.c_str()); - // Invalid auto negotiation mode configured, don't retry - it = consumer.m_toSync.erase(it); - continue; - } - an = autoneg_mode_map[an_str]; - if (an != p.m_autoneg) + if (!p.m_an_cfg || p.m_autoneg != pCfg.autoneg.value) { if (p.m_cap_an < 0) { initPortCapAutoNeg(p); - m_portList[alias] = p; + m_portList[p.m_alias] = p; } if (p.m_cap_an < 1) { SWSS_LOG_ERROR("%s: autoneg is not supported (cap=%d)", p.m_alias.c_str(), p.m_cap_an); // autoneg is not supported, don't retry - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); continue; } if (p.m_admin_state_up) @@ -3395,439 +3513,498 @@ void PortsOrch::doPortTask(Consumer &consumer) /* Bring port down before applying speed */ if (!setPortAdminStatus(p, false)) { - SWSS_LOG_ERROR("Failed to set port %s admin status DOWN to set port autoneg mode", alias.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s admin status DOWN to set port autoneg mode", + p.m_alias.c_str() + ); it++; continue; } p.m_admin_state_up = false; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } - auto status = setPortAutoNeg(p.m_port_id, an); + auto status = setPortAutoNeg(p, pCfg.autoneg.value); if (status != task_success) { - SWSS_LOG_ERROR("Failed to set port %s AN from %d to %d", alias.c_str(), p.m_autoneg, an); + SWSS_LOG_ERROR( + "Failed to set port %s AN from %d to %d", + p.m_alias.c_str(), p.m_autoneg, pCfg.autoneg.value + ); if (status == task_need_retry) { it++; } else { - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); } continue; } - SWSS_LOG_NOTICE("Set port %s AutoNeg from %d to %d", alias.c_str(), p.m_autoneg, an); - p.m_autoneg = static_cast(an); - m_portList[alias] = p; + + p.m_autoneg = pCfg.autoneg.value; + p.m_an_cfg = true; + m_portList[p.m_alias] = p; m_portStateTable.hdel(p.m_alias, "rmt_adv_speeds"); - updatePortStatePoll(p, PORT_STATE_POLL_AN, (an > 0)); + updatePortStatePoll(p, PORT_STATE_POLL_AN, pCfg.autoneg.value); + + SWSS_LOG_NOTICE( + "Set port %s autoneg to %s", + p.m_alias.c_str(), m_portHlpr.getAutonegStr(pCfg).c_str() + ); } } - if (!lt_str.empty() && (p.m_type == Port::PHY)) + if (pCfg.link_training.is_set) { - if (link_training_mode_map.find(lt_str) == link_training_mode_map.end()) - { - SWSS_LOG_ERROR("Failed to parse LT value: %s", lt_str.c_str()); - // Invalid link training mode configured, don't retry - it = consumer.m_toSync.erase(it); - continue; - } - - lt = link_training_mode_map[lt_str]; - if (lt != p.m_link_training) + if (!p.m_lt_cfg || ((p.m_link_training != pCfg.link_training.value) && (p.m_type == Port::PHY))) { if (p.m_cap_lt < 0) { initPortCapLinkTraining(p); - m_portList[alias] = p; + m_portList[p.m_alias] = p; } if (p.m_cap_lt < 1) { - SWSS_LOG_WARN("%s: LT is not supported(cap=%d)", alias.c_str(), p.m_cap_lt); + SWSS_LOG_WARN("%s: LT is not supported(cap=%d)", p.m_alias.c_str(), p.m_cap_lt); // Don't retry - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); continue; } - auto status = setPortLinkTraining(p, lt > 0 ? true : false); + auto status = setPortLinkTraining(p, pCfg.link_training.value); if (status != task_success) { - SWSS_LOG_ERROR("Failed to set port %s LT from %d to %d", alias.c_str(), p.m_link_training, lt); + SWSS_LOG_ERROR( + "Failed to set port %s LT from %d to %d", + p.m_alias.c_str(), p.m_link_training, pCfg.link_training.value + ); if (status == task_need_retry) { it++; } else { - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); } continue; } - m_portStateTable.hset(alias, "link_training_status", lt_str); - SWSS_LOG_NOTICE("Set port %s LT from %d to %d", alias.c_str(), p.m_link_training, lt); - p.m_link_training = lt; - m_portList[alias] = p; - updatePortStatePoll(p, PORT_STATE_POLL_LT, (lt > 0)); + + m_portStateTable.hset(p.m_alias, "link_training_status", m_portHlpr.getLinkTrainingStr(pCfg)); + p.m_link_training = pCfg.link_training.value; + p.m_lt_cfg = true; + m_portList[p.m_alias] = p; + updatePortStatePoll(p, PORT_STATE_POLL_LT, pCfg.link_training.value); // Restore pre-emphasis when LT is transitioned from ON to OFF - if ((p.m_link_training < 1) && (serdes_attr.size() == 0)) + if (!p.m_link_training && serdes_attr.empty()) { serdes_attr = p.m_preemphasis; } + + SWSS_LOG_NOTICE( + "Set port %s link training to %s", + p.m_alias.c_str(), m_portHlpr.getLinkTrainingStr(pCfg).c_str() + ); } } - if (speed != 0) + if (pCfg.speed.is_set) { - if (speed != p.m_speed) + if (p.m_speed != pCfg.speed.value) { - if (!isSpeedSupported(alias, p.m_port_id, speed)) + if (!isSpeedSupported(p.m_alias, p.m_port_id, pCfg.speed.value)) { - SWSS_LOG_ERROR("Unsupported port speed %u", speed); + SWSS_LOG_ERROR( + "Unsupported port %s speed %u", + p.m_alias.c_str(), pCfg.speed.value + ); // Speed not supported, dont retry - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); continue; } - // for backward compatible, if p.m_autoneg != 1, toggle admin status - if (p.m_admin_state_up && p.m_autoneg != 1) + // for backward compatible, if autoneg is off, toggle admin status + if (p.m_admin_state_up && !p.m_autoneg) { /* Bring port down before applying speed */ if (!setPortAdminStatus(p, false)) { - SWSS_LOG_ERROR("Failed to set port %s admin status DOWN to set speed", alias.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s admin status DOWN to set speed", + p.m_alias.c_str() + ); it++; continue; } p.m_admin_state_up = false; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } - auto status = setPortSpeed(p, speed); + auto status = setPortSpeed(p, pCfg.speed.value); if (status != task_success) { - SWSS_LOG_ERROR("Failed to set port %s speed from %u to %u", alias.c_str(), p.m_speed, speed); + SWSS_LOG_ERROR( + "Failed to set port %s speed from %u to %u", + p.m_alias.c_str(), p.m_speed, pCfg.speed.value + ); if (status == task_need_retry) { it++; } else { - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); } continue; } - SWSS_LOG_NOTICE("Set port %s speed from %u to %u", alias.c_str(), p.m_speed, speed); - p.m_speed = speed; - m_portList[alias] = p; + p.m_speed = pCfg.speed.value; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s speed to %u", + p.m_alias.c_str(), pCfg.speed.value + ); } else { /* Always update Gearbox speed on Gearbox ports */ - setGearboxPortsAttr(p, SAI_PORT_ATTR_SPEED, &speed); + setGearboxPortsAttr(p, SAI_PORT_ATTR_SPEED, &pCfg.speed.value); } } - if (!adv_speeds_str.empty()) + if (pCfg.adv_speeds.is_set) { - boost::to_lower(adv_speeds_str); - if (!getPortAdvSpeedsVal(adv_speeds_str, adv_speeds)) + if (!p.m_adv_speed_cfg || p.m_adv_speeds != pCfg.adv_speeds.value) { - // Invalid advertised speeds configured, dont retry - it = consumer.m_toSync.erase(it); - continue; - } - - if (adv_speeds != p.m_adv_speeds) - { - if (p.m_admin_state_up && p.m_autoneg == 1) + if (p.m_admin_state_up && p.m_autoneg) { /* Bring port down before applying speed */ if (!setPortAdminStatus(p, false)) { - SWSS_LOG_ERROR("Failed to set port %s admin status DOWN to set interface type", alias.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s admin status DOWN to set interface type", + p.m_alias.c_str() + ); it++; continue; } p.m_admin_state_up = false; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } + auto adv_speeds = swss::join(',', pCfg.adv_speeds.value.begin(), pCfg.adv_speeds.value.end()); auto ori_adv_speeds = swss::join(',', p.m_adv_speeds.begin(), p.m_adv_speeds.end()); - auto status = setPortAdvSpeeds(p.m_port_id, adv_speeds); + auto status = setPortAdvSpeeds(p, pCfg.adv_speeds.value); if (status != task_success) { - - SWSS_LOG_ERROR("Failed to set port %s advertised speed from %s to %s", alias.c_str(), - ori_adv_speeds.c_str(), - adv_speeds_str.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s advertised speed from %s to %s", + p.m_alias.c_str(), ori_adv_speeds.c_str(), adv_speeds.c_str() + ); if (status == task_need_retry) { it++; } else { - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); } continue; } - SWSS_LOG_NOTICE("Set port %s advertised speed from %s to %s", alias.c_str(), - ori_adv_speeds.c_str(), - adv_speeds_str.c_str()); - p.m_adv_speeds.swap(adv_speeds); - m_portList[alias] = p; + + p.m_adv_speeds = pCfg.adv_speeds.value; + p.m_adv_speed_cfg = true; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s advertised speed from %s to %s", + p.m_alias.c_str(), ori_adv_speeds.c_str(), adv_speeds.c_str() + ); } } - if (!interface_type_str.empty()) + if (pCfg.interface_type.is_set) { - boost::to_lower(interface_type_str); - if (!getPortInterfaceTypeVal(interface_type_str, interface_type)) + if (!p.m_intf_cfg || p.m_interface_type != pCfg.interface_type.value) { - // Invalid interface type configured, dont retry - it = consumer.m_toSync.erase(it); - continue; - } - - if (interface_type != p.m_interface_type) - { - if (p.m_admin_state_up && p.m_autoneg == 0) + if (p.m_admin_state_up && !p.m_autoneg) { /* Bring port down before applying speed */ if (!setPortAdminStatus(p, false)) { - SWSS_LOG_ERROR("Failed to set port %s admin status DOWN to set interface type", alias.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s admin status DOWN to set interface type", + p.m_alias.c_str() + ); it++; continue; } p.m_admin_state_up = false; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } - auto status = setPortInterfaceType(p.m_port_id, interface_type); + auto status = setPortInterfaceType(p, pCfg.interface_type.value); if (status != task_success) { - SWSS_LOG_ERROR("Failed to set port %s interface type to %s", alias.c_str(), interface_type_str.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s interface type to %s", + p.m_alias.c_str(), m_portHlpr.getPortInterfaceTypeStr(pCfg).c_str() + ); if (status == task_need_retry) { it++; } else { - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); } continue; } - SWSS_LOG_NOTICE("Set port %s interface type to %s", alias.c_str(), interface_type_str.c_str()); - p.m_interface_type = interface_type; - m_portList[alias] = p; + p.m_interface_type = pCfg.interface_type.value; + p.m_intf_cfg = true; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s interface type to %s", + p.m_alias.c_str(), m_portHlpr.getPortInterfaceTypeStr(pCfg).c_str() + ); } } - if (!adv_interface_types_str.empty()) + if (pCfg.adv_interface_types.is_set) { - boost::to_lower(adv_interface_types_str); - if (!getPortAdvInterfaceTypesVal(adv_interface_types_str, adv_interface_types)) - { - // Invalid advertised interface types configured, dont retry - it = consumer.m_toSync.erase(it); - continue; - } - - if (adv_interface_types != p.m_adv_interface_types) + if (!p.m_adv_intf_cfg || p.m_adv_interface_types != pCfg.adv_interface_types.value) { - if (p.m_admin_state_up && p.m_autoneg == 1) + if (p.m_admin_state_up && p.m_autoneg) { /* Bring port down before applying speed */ if (!setPortAdminStatus(p, false)) { - SWSS_LOG_ERROR("Failed to set port %s admin status DOWN to set interface type", alias.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s admin status DOWN to set interface type", + p.m_alias.c_str() + ); it++; continue; } p.m_admin_state_up = false; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } - auto status = setPortAdvInterfaceTypes(p.m_port_id, adv_interface_types); + auto status = setPortAdvInterfaceTypes(p, pCfg.adv_interface_types.value); if (status != task_success) { - SWSS_LOG_ERROR("Failed to set port %s advertised interface type to %s", alias.c_str(), adv_interface_types_str.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s advertised interface types to %s", + p.m_alias.c_str(), m_portHlpr.getAdvInterfaceTypesStr(pCfg).c_str() + ); if (status == task_need_retry) { it++; } else { - it = consumer.m_toSync.erase(it); + it = taskMap.erase(it); } continue; } - SWSS_LOG_NOTICE("Set port %s advertised interface type to %s", alias.c_str(), adv_interface_types_str.c_str()); - p.m_adv_interface_types.swap(adv_interface_types); - m_portList[alias] = p; + p.m_adv_interface_types = pCfg.adv_interface_types.value; + p.m_adv_intf_cfg = true; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s advertised interface type to %s", + p.m_alias.c_str(), m_portHlpr.getAdvInterfaceTypesStr(pCfg).c_str() + ); } } - if (mtu != 0 && mtu != p.m_mtu) + if (pCfg.mtu.is_set) { - if (setPortMtu(p, mtu)) + if (p.m_mtu != pCfg.mtu.value) { - p.m_mtu = mtu; - m_portList[alias] = p; - SWSS_LOG_NOTICE("Set port %s MTU to %u", alias.c_str(), mtu); + if (!setPortMtu(p, pCfg.mtu.value)) + { + SWSS_LOG_ERROR( + "Failed to set port %s MTU to %u", + p.m_alias.c_str(), pCfg.mtu.value + ); + it++; + continue; + } + + p.m_mtu = pCfg.mtu.value; + m_portList[p.m_alias] = p; + if (p.m_rif_id) { gIntfsOrch->setRouterIntfsMtu(p); } + // Sub interfaces inherit parent physical port mtu - updateChildPortsMtu(p, mtu); - } - else - { - SWSS_LOG_ERROR("Failed to set port %s MTU to %u", alias.c_str(), mtu); - it++; - continue; + updateChildPortsMtu(p, pCfg.mtu.value); + + SWSS_LOG_NOTICE( + "Set port %s MTU to %u", + p.m_alias.c_str(), pCfg.mtu.value + ); } } - if (tpid != 0 && tpid != p.m_tpid) + if (pCfg.tpid.is_set) { - SWSS_LOG_DEBUG("Set port %s TPID to 0x%x", alias.c_str(), tpid); - if (setPortTpid(p.m_port_id, tpid)) - { - p.m_tpid = tpid; - m_portList[alias] = p; - } - else + if (p.m_tpid != pCfg.tpid.value) { - SWSS_LOG_ERROR("Failed to set port %s TPID to 0x%x", alias.c_str(), tpid); - it++; - continue; + if (!setPortTpid(p, pCfg.tpid.value)) + { + SWSS_LOG_ERROR( + "Failed to set port %s TPID to 0x%x", + p.m_alias.c_str(), pCfg.tpid.value + ); + it++; + continue; + } + + p.m_tpid = pCfg.tpid.value; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s TPID to 0x%x", + p.m_alias.c_str(), pCfg.tpid.value + ); } } - if (!fec_mode.empty()) + if (pCfg.fec.is_set) { - if (fec_mode_map.find(fec_mode) != fec_mode_map.end()) + /* reset fec mode upon mode change */ + if (!p.m_fec_cfg || p.m_fec_mode != pCfg.fec.value) { - /* reset fec mode upon mode change */ - if (!p.m_fec_cfg || p.m_fec_mode != fec_mode_map[fec_mode]) + if (!isFecModeSupported(p, pCfg.fec.value)) { - if (p.m_admin_state_up) - { - /* Bring port down before applying fec mode*/ - if (!setPortAdminStatus(p, false)) - { - SWSS_LOG_ERROR("Failed to set port %s admin status DOWN to set fec mode", alias.c_str()); - it++; - continue; - } - - p.m_admin_state_up = false; - p.m_fec_mode = fec_mode_map[fec_mode]; - p.m_fec_cfg = true; - - if (setPortFec(p, fec_mode)) - { - m_portList[alias] = p; - } - else - { - it++; - continue; - } - } - else + SWSS_LOG_ERROR( + "Unsupported port %s FEC mode %s", + p.m_alias.c_str(), m_portHlpr.getFecStr(pCfg).c_str() + ); + // FEC mode is not supported, don't retry + it = taskMap.erase(it); + continue; + } + + if (p.m_admin_state_up) + { + /* Bring port down before applying fec mode*/ + if (!setPortAdminStatus(p, false)) { - /* Port is already down, setting fec mode*/ - p.m_fec_mode = fec_mode_map[fec_mode]; - p.m_fec_cfg = true; - if (setPortFec(p, fec_mode)) - { - m_portList[alias] = p; - } - else - { - it++; - continue; - } + SWSS_LOG_ERROR( + "Failed to set port %s admin status DOWN to set fec mode", + p.m_alias.c_str() + ); + it++; + continue; } + + p.m_admin_state_up = false; + m_portList[p.m_alias] = p; } - } - else - { - SWSS_LOG_ERROR("Unknown fec mode %s", fec_mode.c_str()); + + if (!setPortFec(p, pCfg.fec.value)) + { + SWSS_LOG_ERROR( + "Failed to set port %s FEC mode %s", + p.m_alias.c_str(), m_portHlpr.getFecStr(pCfg).c_str() + ); + it++; + continue; + } + + p.m_fec_mode = pCfg.fec.value; + p.m_fec_cfg = true; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s FEC mode to %s", + p.m_alias.c_str(), m_portHlpr.getFecStr(pCfg).c_str() + ); } } - if (!learn_mode.empty() && (p.m_learn_mode != learn_mode)) + if (pCfg.learn_mode.is_set) { - if (p.m_bridge_port_id != SAI_NULL_OBJECT_ID) + if (!p.m_lm_cfg || p.m_learn_mode != pCfg.learn_mode.value) { - if(setBridgePortLearnMode(p, learn_mode)) - { - p.m_learn_mode = learn_mode; - m_portList[alias] = p; - SWSS_LOG_NOTICE("Set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); - } - else + if(!setBridgePortLearnMode(p, pCfg.learn_mode.value)) { - SWSS_LOG_ERROR("Failed to set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + SWSS_LOG_ERROR( + "Failed to set port %s learn mode to %s", + p.m_alias.c_str(), m_portHlpr.getLearnModeStr(pCfg).c_str() + ); it++; continue; } - } - else - { - p.m_learn_mode = learn_mode; - m_portList[alias] = p; - SWSS_LOG_NOTICE("Saved to set port %s learn mode %s", alias.c_str(), learn_mode.c_str()); + p.m_learn_mode = pCfg.learn_mode.value; + p.m_lm_cfg = true; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s learn mode to %s", + p.m_alias.c_str(), m_portHlpr.getLearnModeStr(pCfg).c_str() + ); } } - if (pfc_asym != "") + if (pCfg.pfc_asym.is_set) { - if (setPortPfcAsym(p, pfc_asym)) - { - SWSS_LOG_NOTICE("Set port %s asymmetric PFC to %s", alias.c_str(), pfc_asym.c_str()); - } - else + if (!p.m_pfc_asym_cfg || p.m_pfc_asym != pCfg.pfc_asym.value) { - SWSS_LOG_ERROR("Failed to set port %s asymmetric PFC to %s", alias.c_str(), pfc_asym.c_str()); - it++; - continue; + if (!setPortPfcAsym(p, pCfg.pfc_asym.value)) + { + SWSS_LOG_ERROR( + "Failed to set port %s asymmetric PFC to %s", + p.m_alias.c_str(), m_portHlpr.getPfcAsymStr(pCfg).c_str() + ); + it++; + continue; + } + + p.m_pfc_asym = pCfg.pfc_asym.value; + p.m_pfc_asym_cfg = true; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s asymmetric PFC to %s", + p.m_alias.c_str(), m_portHlpr.getPfcAsymStr(pCfg).c_str() + ); } } - if (serdes_attr.size() != 0) + if (!serdes_attr.empty()) { - if (p.m_link_training > 0) + if (p.m_link_training) { - SWSS_LOG_NOTICE("Save port %s preemphasis for LT", alias.c_str()); + SWSS_LOG_NOTICE("Save port %s preemphasis for LT", p.m_alias.c_str()); p.m_preemphasis = serdes_attr; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } else if (setPortSerdesAttribute(p.m_port_id, gSwitchId, serdes_attr)) { - SWSS_LOG_NOTICE("Set port %s preemphasis is success", alias.c_str()); + SWSS_LOG_NOTICE("Set port %s preemphasis is success", p.m_alias.c_str()); p.m_preemphasis = serdes_attr; - m_portList[alias] = p; + m_portList[p.m_alias] = p; } else { - SWSS_LOG_ERROR("Failed to set port %s pre-emphasis", alias.c_str()); + SWSS_LOG_ERROR("Failed to set port %s pre-emphasis", p.m_alias.c_str()); it++; continue; } @@ -3837,25 +4014,44 @@ void PortsOrch::doPortTask(Consumer &consumer) initHostTxReadyState(p); /* Last step set port admin status */ - if (!admin_status.empty() && (p.m_admin_state_up != (admin_status == "up"))) + if (pCfg.admin_status.is_set) { - if (setPortAdminStatus(p, admin_status == "up")) - { - p.m_admin_state_up = (admin_status == "up"); - m_portList[alias] = p; - SWSS_LOG_NOTICE("Set port %s admin status to %s", alias.c_str(), admin_status.c_str()); - } - else + if (p.m_admin_state_up != pCfg.admin_status.value) { - SWSS_LOG_ERROR("Failed to set port %s admin status to %s", alias.c_str(), admin_status.c_str()); - it++; - continue; + if (!setPortAdminStatus(p, pCfg.admin_status.value)) + { + SWSS_LOG_ERROR( + "Failed to set port %s admin status to %s", + p.m_alias.c_str(), m_portHlpr.getAdminStatusStr(pCfg).c_str() + ); + it++; + continue; + } + + p.m_admin_state_up = pCfg.admin_status.value; + m_portList[p.m_alias] = p; + + SWSS_LOG_NOTICE( + "Set port %s admin status to %s", + p.m_alias.c_str(), m_portHlpr.getAdminStatusStr(pCfg).c_str() + ); } } } } else if (op == DEL_COMMAND) { + Port p; + if (!getPort(pCfg.key, p)) + { + SWSS_LOG_ERROR("Failed to remove port: alias %s doesn't exist", pCfg.key.c_str()); + m_portConfigMap.erase(pCfg.key); + it = taskMap.erase(it); + continue; + } + + const auto &alias = pCfg.key; + if (m_port_ref_count[alias] > 0) { SWSS_LOG_WARN("Unable to remove port %s: ref count %u", alias.c_str(), m_port_ref_count[alias]); @@ -3912,8 +4108,11 @@ void PortsOrch::doPortTask(Consumer &consumer) removePortFromPortListMap(port_id); /* Delete port from port list */ + m_portConfigMap.erase(alias); m_portList.erase(alias); saiOidToAlias.erase(port_id); + + SWSS_LOG_NOTICE("Removed port %s", alias.c_str()); } else { @@ -4179,7 +4378,8 @@ void PortsOrch::doLagTask(Consumer &consumer) { // Retrieve attributes uint32_t mtu = 0; - string learn_mode; + string learn_mode_str; + sai_bridge_port_fdb_learning_mode_t learn_mode = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW; string operation_status; uint32_t lag_id = 0; int32_t switch_id = -1; @@ -4194,7 +4394,17 @@ void PortsOrch::doLagTask(Consumer &consumer) } else if (fvField(i) == "learn_mode") { - learn_mode = fvValue(i); + learn_mode_str = fvValue(i); + + const auto &cit = learn_mode_map.find(learn_mode_str); + if (cit == learn_mode_map.cend()) + { + SWSS_LOG_ERROR("Invalid MAC learn mode: %s", learn_mode_str.c_str()); + it++; + continue; + } + + learn_mode = cit->second; } else if (fvField(i) == "oper_status") { @@ -4294,7 +4504,7 @@ void PortsOrch::doLagTask(Consumer &consumer) } } - if (!learn_mode.empty() && (l.m_learn_mode != learn_mode)) + if (!learn_mode_str.empty() && (l.m_learn_mode != learn_mode)) { if (l.m_bridge_port_id != SAI_NULL_OBJECT_ID) { @@ -4302,11 +4512,11 @@ void PortsOrch::doLagTask(Consumer &consumer) { l.m_learn_mode = learn_mode; m_portList[alias] = l; - SWSS_LOG_NOTICE("Set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + SWSS_LOG_NOTICE("Set port %s learn mode to %s", alias.c_str(), learn_mode_str.c_str()); } else { - SWSS_LOG_ERROR("Failed to set port %s learn mode to %s", alias.c_str(), learn_mode.c_str()); + SWSS_LOG_ERROR("Failed to set port %s learn mode to %s", alias.c_str(), learn_mode_str.c_str()); it++; continue; } @@ -4316,7 +4526,7 @@ void PortsOrch::doLagTask(Consumer &consumer) l.m_learn_mode = learn_mode; m_portList[alias] = l; - SWSS_LOG_NOTICE("Saved to set port %s learn mode %s", alias.c_str(), learn_mode.c_str()); + SWSS_LOG_NOTICE("Saved to set port %s learn mode %s", alias.c_str(), learn_mode_str.c_str()); } } } @@ -5013,15 +5223,7 @@ bool PortsOrch::addBridgePort(Port &port) /* And with hardware FDB learning mode set to HW (explicit default value) */ attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE; - auto found = learn_mode_map.find(port.m_learn_mode); - if (found == learn_mode_map.end()) - { - attr.value.s32 = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW; - } - else - { - attr.value.s32 = found->second; - } + attr.value.s32 = port.m_learn_mode; attrs.push_back(attr); sai_status_t status = sai_bridge_api->create_bridge_port(&port.m_bridge_port_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); @@ -5113,7 +5315,7 @@ bool PortsOrch::removeBridgePort(Port &port) return true; } -bool PortsOrch::setBridgePortLearnMode(Port &port, string learn_mode) +bool PortsOrch::setBridgePortLearnMode(Port &port, sai_bridge_port_fdb_learning_mode_t learn_mode) { SWSS_LOG_ENTER(); @@ -5122,17 +5324,10 @@ bool PortsOrch::setBridgePortLearnMode(Port &port, string learn_mode) return true; } - auto found = learn_mode_map.find(learn_mode); - if (found == learn_mode_map.end()) - { - SWSS_LOG_ERROR("Incorrect MAC learn mode: %s", learn_mode.c_str()); - return false; - } - /* Set bridge port learning mode */ sai_attribute_t attr; attr.id = SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE; - attr.value.s32 = found->second; + attr.value.s32 = learn_mode; sai_status_t status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); if (status != SAI_STATUS_SUCCESS) @@ -5146,7 +5341,7 @@ bool PortsOrch::setBridgePortLearnMode(Port &port, string learn_mode) } } - SWSS_LOG_NOTICE("Set bridge port %s learning mode %s", port.m_alias.c_str(), learn_mode.c_str()); + SWSS_LOG_NOTICE("Set bridge port %s learning mode %d", port.m_alias.c_str(), learn_mode); return true; } @@ -6117,11 +6312,11 @@ bool PortsOrch::addTunnel(string tunnel_alias, sai_object_id_t tunnel_id, bool h tunnel.m_tunnel_id = tunnel_id; if (hwlearning) { - tunnel.m_learn_mode = "hardware"; + tunnel.m_learn_mode = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW; } else { - tunnel.m_learn_mode = "disable"; + tunnel.m_learn_mode = SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE; } m_portList[tunnel_alias] = tunnel; @@ -7297,86 +7492,6 @@ void PortsOrch::getPortSerdesVal(const std::string& val_str, } } -bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, - std::vector &speed_values) -{ - SWSS_LOG_ENTER(); - - if (val_str == "all") - { - return true; - } - - uint32_t speed_val; - std::string speed_str; - std::istringstream iss(val_str); - - try - { - while (std::getline(iss, speed_str, ',')) - { - speed_val = (uint32_t)std::stoul(speed_str); - speed_values.push_back(speed_val); - } - } - catch (const std::invalid_argument &e) - { - SWSS_LOG_ERROR("Failed to parse adv_speeds value: %s", val_str.c_str()); - return false; - } - std::sort(speed_values.begin(), speed_values.end()); - return true; -} - -bool PortsOrch::getPortInterfaceTypeVal(const std::string &s, - sai_port_interface_type_t &interface_type) -{ - SWSS_LOG_ENTER(); - - auto iter = interface_type_map_for_an.find(s); - if (iter != interface_type_map_for_an.end()) - { - interface_type = interface_type_map_for_an[s]; - return true; - } - else - { - const std::string &validInterfaceTypes = getValidInterfaceTypes(); - SWSS_LOG_ERROR("Failed to parse interface_type value %s, valid interface type includes: %s", - s.c_str(), validInterfaceTypes.c_str()); - return false; - } -} - -bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, - std::vector &type_values) -{ - SWSS_LOG_ENTER(); - if (val_str == "all") - { - return true; - } - - sai_port_interface_type_t interface_type ; - std::string type_str; - std::istringstream iss(val_str); - bool valid; - - while (std::getline(iss, type_str, ',')) - { - valid = getPortInterfaceTypeVal(type_str, interface_type); - if (!valid) { - const std::string &validInterfaceTypes = getValidInterfaceTypes(); - SWSS_LOG_ERROR("Failed to parse adv_interface_types value %s, valid interface type includes: %s", - val_str.c_str(), validInterfaceTypes.c_str()); - return false; - } - type_values.push_back(static_cast(interface_type)); - } - std::sort(type_values.begin(), type_values.end()); - return true; -} - /* Bring up/down Vlan interface associated with L3 VNI*/ bool PortsOrch::updateL3VniStatus(uint16_t vlan_id, bool isUp) { @@ -7855,7 +7970,7 @@ bool PortsOrch::getSystemPorts() return true; } -bool PortsOrch::getRecircPort(Port &port, string role) +bool PortsOrch::getRecircPort(Port &port, Port::Role role) { for (auto it = m_recircPortRole.begin(); it != m_recircPortRole.end(); it++) { @@ -7864,7 +7979,12 @@ bool PortsOrch::getRecircPort(Port &port, string role) return getPort(it->first, port); } } - SWSS_LOG_ERROR("Failed to find recirc port with role %s", role.c_str()); + + SWSS_LOG_ERROR( + "Failed to find recirc port %s with role %d", + port.m_alias.c_str(), static_cast(role) + ); + return false; } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 843dcc8956..5c5800a654 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -16,6 +16,7 @@ #include "flexcounterorch.h" #include "events.h" +#include "port/porthlpr.h" #define FCS_LEN 4 #define VLAN_TAG_LEN 4 @@ -29,7 +30,7 @@ #define PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP "PG_DROP_STAT_COUNTER" typedef std::vector PortSupportedSpeeds; -typedef std::set PortSupportedFecModes; +typedef std::set PortSupportedFecModes; static const map oper_status_strings = { @@ -105,6 +106,15 @@ struct queueInfo sai_uint8_t index; }; +template +struct PortCapability +{ + bool supported = false; + T data; +}; + +typedef PortCapability PortFecModeCapability_t; + class PortsOrch : public Orch, public Subject { public: @@ -206,7 +216,7 @@ class PortsOrch : public Orch, public Subject bool setVoqInbandIntf(string &alias, string &type); bool getPortVlanMembers(Port &port, vlan_members_t &vlan_members); - bool getRecircPort(Port &p, string role); + bool getRecircPort(Port &p, Port::Role role); const gearbox_phy_t* getGearboxPhy(const Port &port); @@ -259,9 +269,10 @@ class PortsOrch : public Orch, public Subject shared_ptr m_gb_counter_db; unique_ptr m_gbcounterTable; + // Supported speeds on the system side. std::map m_portSupportedSpeeds; // Supported FEC modes on the system side. - std::map m_portSupportedFecModes; + std::map m_portSupportedFecModes; bool m_initDone = false; Port m_cpuPort; @@ -292,8 +303,8 @@ class PortsOrch : public Orch, public Subject port_config_state_t m_portConfigState = PORT_CONFIG_MISSING; sai_uint32_t m_portCount; - map, sai_object_id_t> m_portListLaneMap; - map, tuple> m_lanesAliasSpeedMap; + map, sai_object_id_t> m_portListLaneMap; + map, PortConfig> m_lanesAliasSpeedMap; map m_portList; map m_portVlanMember; map> m_port_voq_ids; @@ -302,7 +313,7 @@ class PortsOrch : public Orch, public Subject * coming from SAI */ unordered_map saiOidToAlias; - unordered_map m_portOidToIndex; + unordered_map m_portOidToIndex; map m_port_ref_count; unordered_set m_pendingPortSet; const uint32_t max_flood_control_types = 4; @@ -340,7 +351,7 @@ class PortsOrch : public Orch, public Subject bool addHostIntfs(Port &port, string alias, sai_object_id_t &host_intfs_id); bool setHostIntfsStripTag(Port &port, sai_hostif_vlan_tag_t strip); - bool setBridgePortLearnMode(Port &port, string learn_mode); + bool setBridgePortLearnMode(Port &port, sai_bridge_port_fdb_learning_mode_t learn_mode); bool addVlan(string vlan); bool removeVlan(Port vlan); @@ -353,9 +364,9 @@ class PortsOrch : public Orch, public Subject bool setCollectionOnLagMember(Port &lagMember, bool enableCollection); bool setDistributionOnLagMember(Port &lagMember, bool enableDistribution); - bool addPort(const set &lane_set, uint32_t speed, int an=0, string fec=""); + bool addPort(const PortConfig &port); sai_status_t removePort(sai_object_id_t port_id); - bool initPort(const string &alias, const string &role, const int index, const set &lane_set); + bool initPort(const PortConfig &port); void deInitPort(string alias, sai_object_id_t port_id); void initPortCapAutoNeg(Port &port); @@ -365,20 +376,22 @@ class PortsOrch : public Orch, public Subject bool getPortAdminStatus(sai_object_id_t id, bool& up); bool getPortMtu(const Port& port, sai_uint32_t &mtu); bool setPortMtu(const Port& port, sai_uint32_t mtu); - bool setPortTpid(sai_object_id_t id, sai_uint16_t tpid); + bool setPortTpid(Port &port, sai_uint16_t tpid); bool setPortPvid (Port &port, sai_uint32_t pvid); bool getPortPvid(Port &port, sai_uint32_t &pvid); - bool setPortFec(Port &port, std::string &mode); - bool setPortPfcAsym(Port &port, string pfc_asym); + bool setPortFec(Port &port, sai_port_fec_mode_t fec_mode); + bool setPortPfcAsym(Port &port, sai_port_priority_flow_control_mode_t pfc_asym); bool getDestPortId(sai_object_id_t src_port_id, dest_port_type_t port_type, sai_object_id_t &des_port_id); bool setBridgePortAdminStatus(sai_object_id_t id, bool up); + // Get supported speeds on system side bool isSpeedSupported(const std::string& alias, sai_object_id_t port_id, sai_uint32_t speed); void getPortSupportedSpeeds(const std::string& alias, sai_object_id_t port_id, PortSupportedSpeeds &supported_speeds); void initPortSupportedSpeeds(const std::string& alias, sai_object_id_t port_id); // Get supported FEC modes on system side - void getPortSupportedFecModes(const std::string& alias, sai_object_id_t port_id, PortSupportedFecModes &supported_fecmodes); + bool isFecModeSupported(const Port &port, sai_port_fec_mode_t fec_mode); + sai_status_t getPortSupportedFecModes(PortSupportedFecModes &supported_fecmodes, sai_object_id_t port_id); void initPortSupportedFecModes(const std::string& alias, sai_object_id_t port_id); task_process_status setPortSpeed(Port &port, sai_uint32_t speed); bool getPortSpeed(sai_object_id_t id, sai_uint32_t &speed); @@ -387,7 +400,7 @@ class PortsOrch : public Orch, public Subject bool getPortAdvSpeeds(const Port& port, bool remote, std::vector& speed_list); bool getPortAdvSpeeds(const Port& port, bool remote, string& adv_speeds); - task_process_status setPortAdvSpeeds(sai_object_id_t port_id, std::vector& speed_list); + task_process_status setPortAdvSpeeds(Port &port, std::set &speed_list); bool getQueueTypeAndIndex(sai_object_id_t queue_id, string &type, uint8_t &index); @@ -415,10 +428,9 @@ class PortsOrch : public Orch, public Subject bool m_isPortBufferDropCounterMapGenerated = false; bool isAutoNegEnabled(sai_object_id_t id); - task_process_status setPortAutoNeg(sai_object_id_t id, int an); - bool setPortFecMode(sai_object_id_t id, int fec); - task_process_status setPortInterfaceType(sai_object_id_t id, sai_port_interface_type_t interface_type); - task_process_status setPortAdvInterfaceTypes(sai_object_id_t id, std::vector &interface_types); + task_process_status setPortAutoNeg(Port &port, bool autoneg); + task_process_status setPortInterfaceType(Port &port, sai_port_interface_type_t interface_type); + task_process_status setPortAdvInterfaceTypes(Port &port, std::set &interface_types); task_process_status setPortLinkTraining(const Port& port, bool state); void updatePortOperStatus(Port &port, sai_port_oper_status_t status); @@ -441,10 +453,6 @@ class PortsOrch : public Orch, public Subject void refreshPortStateLinkTraining(const Port &port); void getPortSerdesVal(const std::string& s, std::vector &lane_values, int base = 16); - bool getPortAdvSpeedsVal(const std::string &s, std::vector &speed_values); - bool getPortInterfaceTypeVal(const std::string &s, sai_port_interface_type_t &interface_type); - bool getPortAdvInterfaceTypesVal(const std::string &s, std::vector &type_values); - bool setPortSerdesAttribute(sai_object_id_t port_id, sai_object_id_t switch_id, std::map> &serdes_attr); @@ -456,7 +464,7 @@ class PortsOrch : public Orch, public Subject void initGearbox(); bool initGearboxPort(Port &port); - map m_recircPortRole; + map m_recircPortRole; //map key is tuple of map, sai_object_id_t> m_systemPortOidMap; @@ -474,5 +482,22 @@ class PortsOrch : public Orch, public Subject std::unordered_set generateCounterStats(const string& type, bool gearbox = false); map m_queueInfo; + +private: + void initializeCpuPort(); + void initializePorts(); + + auto getPortConfigState() const -> port_config_state_t; + void setPortConfigState(port_config_state_t value); + + bool addPortBulk(const std::vector &portList); + bool removePortBulk(const std::vector &portList); + +private: + // Port config aggregator + std::unordered_map> m_portConfigMap; + + // Port OA helper + PortHelper m_portHlpr; }; #endif /* SWSS_PORTSORCH_H */ diff --git a/tests/conftest.py b/tests/conftest.py index 0e6a923a2b..b6a81c1dbf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1858,7 +1858,7 @@ def vct(request): @pytest.fixture def testlog(request, dvs): dvs.runcmd(f"logger -t pytest === start test {request.node.nodeid} ===") - yield testlog + yield dvs.runcmd(f"logger -t pytest === finish test {request.node.nodeid} ===") ################# DVSLIB module manager fixtures ############################# @@ -1903,6 +1903,7 @@ def dvs_vlan_manager(request, dvs): @pytest.fixture(scope="class") def dvs_port_manager(request, dvs): request.cls.dvs_port = dvs_port.DVSPort(dvs.get_asic_db(), + dvs.get_app_db(), dvs.get_config_db()) diff --git a/tests/dvslib/dvs_database.py b/tests/dvslib/dvs_database.py index 371b7f61e9..553c0d7710 100644 --- a/tests/dvslib/dvs_database.py +++ b/tests/dvslib/dvs_database.py @@ -6,6 +6,7 @@ """ from typing import Dict, List from swsscommon import swsscommon +from swsscommon.swsscommon import SonicDBConfig from dvslib.dvs_common import wait_for_result, PollingConfig @@ -21,6 +22,12 @@ def __init__(self, db_id: int, connector: str): redis (e.g. UNIX socket, TCP socket, etc.). """ self.db_connection = swsscommon.DBConnector(db_id, connector, 0) + self._separator = SonicDBConfig.getSeparator(self.db_connection) + + @property + def separator(self) -> str: + """Get DB separator.""" + return self._separator def create_entry(self, table_name: str, key: str, entry: Dict[str, str]) -> None: """Add the mapping {`key` -> `entry`} to the specified table. diff --git a/tests/dvslib/dvs_port.py b/tests/dvslib/dvs_port.py index 8c53994242..330245099c 100644 --- a/tests/dvslib/dvs_port.py +++ b/tests/dvslib/dvs_port.py @@ -1,8 +1,44 @@ +"""Utilities for interacting with PORT objects when writing VS tests.""" +from typing import Dict, List +from swsscommon import swsscommon + class DVSPort(object): - def __init__(self, adb, cdb): - self.asic_db = adb - self.config_db = cdb + """Manage PORT objects on the virtual switch.""" + ASIC_DB = swsscommon.ASIC_DB + APPL_DB = swsscommon.APPL_DB + + CFGDB_PORT = "PORT" + APPDB_PORT = "PORT_TABLE" + ASICDB_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_PORT" + + def __init__(self, asicdb, appdb, cfgdb): + self.asic_db = asicdb + self.app_db = appdb + self.config_db = cfgdb + + def create_port_generic( + self, + port_name: str, + lanes: str, + speed: str, + qualifiers: Dict[str, str] = {} + ) -> None: + """Create PORT in Config DB.""" + attr_dict = { + "lanes": lanes, + "speed": speed, + **qualifiers + } + + self.config_db.create_entry(self.CFGDB_PORT, port_name, attr_dict) + + def remove_port_generic( + self, + port_name: str + )-> None: + """Remove PORT from Config DB.""" + self.config_db.delete_entry(self.CFGDB_PORT, port_name) def remove_port(self, port_name): self.config_db.delete_field("CABLE_LENGTH", "AZURE", port_name) @@ -18,3 +54,42 @@ def remove_port(self, port_name): self.config_db.delete_entry("BREAKOUT_CFG|%s" % port_name, "") self.config_db.delete_entry("INTERFACE|%s" % port_name, "") self.config_db.delete_entry("PORT", port_name) + + def update_port( + self, + port_name: str, + attr_dict: Dict[str, str] + ) -> None: + """Update PORT in Config DB.""" + self.config_db.update_entry(self.CFGDB_PORT, port_name, attr_dict) + + def get_port_ids( + self, + expected: int = None, + dbid: int = swsscommon.ASIC_DB + ) -> List[str]: + """Get all of the PORT objects in ASIC/APP DB.""" + conn = None + table = None + + if dbid == swsscommon.ASIC_DB: + conn = self.asic_db + table = self.ASICDB_PORT + elif dbid == swsscommon.APPL_DB: + conn = self.app_db + table = self.APPDB_PORT + else: + raise RuntimeError("Interface not implemented") + + if expected is None: + return conn.get_keys(table) + + return conn.wait_for_n_keys(table, expected) + + def verify_port_count( + self, + expected: int, + dbid: int = swsscommon.ASIC_DB + ) -> None: + """Verify that there are N PORT objects in ASIC/APP DB.""" + self.get_port_ids(expected, dbid) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 3b23b1aeba..522b45110b 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -69,6 +69,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/cbf/nhgmaporch.cpp \ $(top_srcdir)/orchagent/neighorch.cpp \ $(top_srcdir)/orchagent/intfsorch.cpp \ + $(top_srcdir)/orchagent/port/porthlpr.cpp \ $(top_srcdir)/orchagent/portsorch.cpp \ $(top_srcdir)/orchagent/fabricportsorch.cpp \ $(top_srcdir)/orchagent/copporch.cpp \ diff --git a/tests/mock_tests/mock_orchagent_main.cpp b/tests/mock_tests/mock_orchagent_main.cpp index 542e5f3e36..e709824707 100644 --- a/tests/mock_tests/mock_orchagent_main.cpp +++ b/tests/mock_tests/mock_orchagent_main.cpp @@ -1,6 +1,6 @@ extern "C" { -#include "sai.h" -#include "saistatus.h" +#include +#include } #include "orchdaemon.h" diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 1509a407ef..85c97ed78a 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -13,6 +13,9 @@ #include "switchorch.h" #include #undef private +#define private public +#include "warm_restart.h" +#undef private #include @@ -24,6 +27,9 @@ namespace portsorch_test { using namespace std; + // SAI default ports + std::map> defaultPortList; + sai_port_api_t ut_sai_port_api; sai_port_api_t *pold_sai_port_api; sai_switch_api_t ut_sai_switch_api; @@ -215,6 +221,37 @@ namespace portsorch_test sai_bridge_api = org_sai_bridge_api; } + void cleanupPorts(PortsOrch *obj) + { + // Get CPU port + Port p; + obj->getCpuPort(p); + + // Get port list + auto portList = obj->getAllPorts(); + portList.erase(p.m_alias); + + // Generate port config + std::deque kfvList; + + for (const auto &cit : portList) + { + kfvList.push_back({ cit.first, DEL_COMMAND, { } }); + } + + // Refill consumer + auto consumer = dynamic_cast(obj->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(kfvList); + + // Apply configuration + static_cast(obj)->doTask(); + + // Dump pending tasks + std::vector taskList; + obj->dumpPendingTasks(taskList); + ASSERT_TRUE(taskList.empty()); + } + struct PortsOrchTest : public ::testing::Test { shared_ptr m_app_db; @@ -394,6 +431,7 @@ namespace portsorch_test // clear orchs saved in directory gDirectory.m_values.clear(); } + static void SetUpTestCase() { // Init switch and create dependencies @@ -429,6 +467,10 @@ namespace portsorch_test ASSERT_EQ(status, SAI_STATUS_SUCCESS); gVirtualRouterId = attr.value.oid; + + // Get SAI default ports + defaultPortList = ut_helper::getInitialSaiPorts(); + ASSERT_TRUE(!defaultPortList.empty()); } static void TearDownTestCase() @@ -442,6 +484,282 @@ namespace portsorch_test }; + TEST_F(PortsOrchTest, PortBulkCreateRemove) + { + auto portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports + auto &ports = defaultPortList; + ASSERT_TRUE(!ports.empty()); + + // Generate port config + for (std::uint32_t idx1 = 0, idx2 = 1; idx1 < ports.size() * 4; idx1 += 4, idx2++) + { + std::stringstream key; + key << FRONT_PANEL_PORT_PREFIX << idx1; + + std::stringstream alias; + alias << "etp" << idx2; + + std::stringstream index; + index << idx2; + + std::stringstream lanes; + lanes << idx1 << "," << idx1 + 1 << "," << idx1 + 2 << "," << idx1 + 3; + + std::vector fvList = { + { "alias", alias.str() }, + { "index", index.str() }, + { "lanes", lanes.str() }, + { "speed", "100000" }, + { "autoneg", "off" }, + { "adv_speeds", "all" }, + { "interface_type", "none" }, + { "adv_interface_types", "all" }, + { "fec", "rs" }, + { "mtu", "9100" }, + { "tpid", "0x8100" }, + { "pfc_asym", "off" }, + { "admin_status", "up" }, + { "description", "FP port" } + }; + + portTable.set(key.str(), fvList); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", std::to_string(ports.size()) } }); + + // Refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration + static_cast(gPortsOrch)->doTask(); + + // Port count: 32 Data + 1 CPU + ASSERT_EQ(gPortsOrch->getAllPorts().size(), ports.size() + 1); + + // Dump pending tasks + std::vector taskList; + gPortsOrch->dumpPendingTasks(taskList); + ASSERT_TRUE(taskList.empty()); + + // Cleanup ports + cleanupPorts(gPortsOrch); + } + + TEST_F(PortsOrchTest, PortBasicConfig) + { + auto portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports + auto &ports = defaultPortList; + ASSERT_TRUE(!ports.empty()); + + // Generate port config + for (const auto &cit : ports) + { + portTable.set(cit.first, cit.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", std::to_string(ports.size()) } }); + + // Refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration + static_cast(gPortsOrch)->doTask(); + + // Port count: 32 Data + 1 CPU + ASSERT_EQ(gPortsOrch->getAllPorts().size(), ports.size() + 1); + + // Generate port config + std::deque kfvList = {{ + "Ethernet0", + SET_COMMAND, { + { "speed", "100000" }, + { "autoneg", "on" }, + { "adv_speeds", "1000,10000,100000" }, + { "interface_type", "CR" }, + { "adv_interface_types", "CR,CR2,CR4,CR8" }, + { "fec", "fc" }, + { "mtu", "9100" }, + { "tpid", "0x9100" }, + { "pfc_asym", "on" }, + { "link_training", "on" }, + { "admin_status", "up" } + } + }}; + + // Refill consumer + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(kfvList); + + // Apply configuration + static_cast(gPortsOrch)->doTask(); + + // Get port + Port p; + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + + // Verify speed + ASSERT_EQ(p.m_speed, 100000); + + // Verify auto-negotiation + ASSERT_TRUE(p.m_autoneg); + + // Verify advertised speed + std::set adv_speeds = { 1000, 10000, 100000 }; + ASSERT_EQ(p.m_adv_speeds, adv_speeds); + + // Verify interface type + ASSERT_EQ(p.m_interface_type, SAI_PORT_INTERFACE_TYPE_CR); + + // Verify advertised interface type + std::set adv_interface_types = { + SAI_PORT_INTERFACE_TYPE_CR, + SAI_PORT_INTERFACE_TYPE_CR2, + SAI_PORT_INTERFACE_TYPE_CR4, + SAI_PORT_INTERFACE_TYPE_CR8 + }; + ASSERT_EQ(p.m_adv_interface_types, adv_interface_types); + + // Verify FEC + ASSERT_EQ(p.m_fec_mode, SAI_PORT_FEC_MODE_FC); + + // Verify MTU + ASSERT_EQ(p.m_mtu, 9100); + + // Verify TPID + ASSERT_EQ(p.m_tpid, 0x9100); + + // Verify asymmetric PFC + ASSERT_EQ(p.m_pfc_asym, SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE); + + // Verify link training + ASSERT_TRUE(p.m_link_training); + + // Verify admin status + ASSERT_TRUE(p.m_admin_state_up); + + // Dump pending tasks + std::vector taskList; + gPortsOrch->dumpPendingTasks(taskList); + ASSERT_TRUE(taskList.empty()); + + // Cleanup ports + cleanupPorts(gPortsOrch); + } + + TEST_F(PortsOrchTest, PortAdvancedConfig) + { + auto portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports + auto &ports = defaultPortList; + ASSERT_TRUE(!ports.empty()); + + // Generate port config + for (const auto &cit : ports) + { + portTable.set(cit.first, cit.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", std::to_string(ports.size()) } }); + + // Refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration + static_cast(gPortsOrch)->doTask(); + + // Port count: 32 Data + 1 CPU + ASSERT_EQ(gPortsOrch->getAllPorts().size(), ports.size() + 1); + + // Generate port serdes config + std::deque kfvList = {{ + "Ethernet0", + SET_COMMAND, { + { "preemphasis", "0xcad0,0xc6e0,0xc6e0,0xd2b0" }, + { "idriver", "0x5,0x3,0x4,0x1" }, + { "ipredriver", "0x1,0x4,0x3,0x5" }, + { "pre1", "0xfff0,0xfff2,0xfff1,0xfff3" }, + { "pre2", "0xfff0,0xfff2,0xfff1,0xfff3" }, + { "pre3", "0xfff0,0xfff2,0xfff1,0xfff3" }, + { "main", "0x90,0x92,0x91,0x93" }, + { "post1", "0x10,0x12,0x11,0x13" }, + { "post2", "0x10,0x12,0x11,0x13" }, + { "post3", "0x10,0x12,0x11,0x13" }, + { "attn", "0x80,0x82,0x81,0x83" } + } + }}; + + // Refill consumer + auto consumer = dynamic_cast(gPortsOrch->getExecutor(APP_PORT_TABLE_NAME)); + consumer->addToSync(kfvList); + + // Apply configuration + static_cast(gPortsOrch)->doTask(); + + // Get port + Port p; + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + + // Verify preemphasis + std::vector preemphasis = { 0xcad0, 0xc6e0, 0xc6e0, 0xd2b0 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_PREEMPHASIS), preemphasis); + + // Verify idriver + std::vector idriver = { 0x5, 0x3, 0x4, 0x1 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_IDRIVER), idriver); + + // Verify ipredriver + std::vector ipredriver = { 0x1, 0x4, 0x3, 0x5 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_IPREDRIVER), ipredriver); + + // Verify pre1 + std::vector pre1 = { 0xfff0, 0xfff2, 0xfff1, 0xfff3 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_PRE1), pre1); + + // Verify pre2 + std::vector pre2 = { 0xfff0, 0xfff2, 0xfff1, 0xfff3 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_PRE2), pre2); + + // Verify pre3 + std::vector pre3 = { 0xfff0, 0xfff2, 0xfff1, 0xfff3 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_PRE3), pre3); + + // Verify main + std::vector main = { 0x90, 0x92, 0x91, 0x93 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_MAIN), main); + + // Verify post1 + std::vector post1 = { 0x10, 0x12, 0x11, 0x13 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_POST1), post1); + + // Verify post2 + std::vector post2 = { 0x10, 0x12, 0x11, 0x13 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_POST2), post2); + + // Verify post3 + std::vector post3 = { 0x10, 0x12, 0x11, 0x13 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_POST3), post3); + + // Verify attn + std::vector attn = { 0x80, 0x82, 0x81, 0x83 }; + ASSERT_EQ(p.m_preemphasis.at(SAI_PORT_SERDES_ATTR_TX_FIR_ATTN), attn); + + // Dump pending tasks + std::vector taskList; + gPortsOrch->dumpPendingTasks(taskList); + ASSERT_TRUE(taskList.empty()); + + // Cleanup ports + cleanupPorts(gPortsOrch); + } + /** * Test that verifies PortsOrch::getPort() on a port that has been deleted */ @@ -452,7 +770,8 @@ namespace portsorch_test std::deque entries; // Get SAI default ports to populate DB - auto ports = ut_helper::getInitialSaiPorts(); + auto &ports = defaultPortList; + ASSERT_TRUE(!ports.empty()); for (const auto &it : ports) { @@ -818,7 +1137,6 @@ namespace portsorch_test // create ports static_cast(gBufferOrch)->doTask(); - static_cast(gPortsOrch)->doTask(); // Ports are not ready yet @@ -903,6 +1221,12 @@ namespace portsorch_test portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); portTable.set("PortInitDone", { { "lanes", "0" } }); + // warm start, initialize ports ready list + + WarmStart::getInstance().m_enabled = true; + gBufferOrch->initBufferReadyLists(m_app_db.get(), m_config_db.get()); + WarmStart::getInstance().m_enabled = false; + // warm start, bake fill refill consumer gBufferOrch->bake(); diff --git a/tests/test_port_add_remove.py b/tests/test_port_add_remove.py old mode 100755 new mode 100644 index bfc3074d83..54cd6599c9 --- a/tests/test_port_add_remove.py +++ b/tests/test_port_add_remove.py @@ -248,4 +248,278 @@ def test_add_remove_all_the_ports(self, dvs, testlog, scenario): dvs.set_interface_status(PORT_B, port_admin_b) dvs.remove_vlan_member("6", PORT_A) dvs.remove_vlan_member("6", PORT_B) + dvs.remove_ip_address("Vlan6", "6.6.6.1/24") dvs.remove_vlan("6") + + +@pytest.mark.usefixtures("dynamic_buffer") +@pytest.mark.usefixtures("dvs_port_manager") +class TestPortAddRemoveDup(object): + def test_add_remove_with_dup_lanes(self, testlog, dvs): + config_db = dvs.get_config_db() + app_db = dvs.get_app_db() + state_db = dvs.get_state_db() + + # set mmu size + fvs = {"mmu_size": "12766208"} + state_db.create_entry("BUFFER_MAX_PARAM_TABLE", "global", fvs) + + # get port count + port_count = len(self.dvs_port.get_port_ids()) + + # get port info + port_info = config_db.get_entry("PORT", PORT_A) + + # remove buffer pg cfg for the port + pgs = config_db.get_keys("BUFFER_PG") + buffer_pgs = {} + for key in pgs: + if PORT_A in key: + buffer_pgs[key] = config_db.get_entry("BUFFER_PG", key) + config_db.delete_entry("BUFFER_PG", key) + app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", key.replace(config_db.separator, app_db.separator)) + + # remove buffer queue cfg for the port + queues = config_db.get_keys("BUFFER_QUEUE") + buffer_queues = {} + for key in queues: + if PORT_A in key: + buffer_queues[key] = config_db.get_entry("BUFFER_QUEUE", key) + config_db.delete_entry("BUFFER_QUEUE", key) + app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", key.replace(config_db.separator, app_db.separator)) + + # shutdown port + dvs.port_admin_set(PORT_A, "down") + + # remove port + self.dvs_port.remove_port_generic(PORT_A) + self.dvs_port.verify_port_count(port_count-1) + + # make port config with duplicate lanes + dup_lanes = port_info["lanes"] + dup_lanes += ",{}".format(port_info["lanes"].split(",")[-1]) + + # add port + self.dvs_port.create_port_generic(PORT_A, dup_lanes, port_info["speed"]) + self.dvs_port.verify_port_count(port_count) + + # shutdown port + dvs.port_admin_set(PORT_A, "down") + + # remove port + self.dvs_port.remove_port_generic(PORT_A) + self.dvs_port.verify_port_count(port_count-1) + + # make port config + port_lanes = port_info.pop("lanes") + port_speed = port_info.pop("speed") + + # re-add port + self.dvs_port.create_port_generic(PORT_A, port_lanes, port_speed, port_info) + self.dvs_port.verify_port_count(port_count) + + # re-add buffer pg and queue cfg to the port + for key, pg in buffer_pgs.items(): + config_db.update_entry("BUFFER_PG", key, pg) + app_db.wait_for_entry("BUFFER_PG_TABLE", key.replace(config_db.separator, app_db.separator)) + + for key, queue in buffer_queues.items(): + config_db.update_entry("BUFFER_QUEUE", key, queue) + app_db.wait_for_entry("BUFFER_QUEUE_TABLE", key.replace(config_db.separator, app_db.separator)) + + +@pytest.mark.usefixtures("dvs_port_manager") +class TestPortAddRemoveInvalidMandatoryParam(object): + @pytest.mark.parametrize( + "port,lanes,speed", [ + pytest.param("Ethernet1000", "", "10000", id="empty-lanes-list"), + pytest.param("Ethernet1004", "1004,x,1006,1007", "10000", id="invalid-lanes-list"), + pytest.param("Ethernet1008", "1008,1009,1010,1011", "", id="empty-speed"), + pytest.param("Ethernet1012", "1012,1013,1014,1015", "invalid", id="invalid-speed"), + pytest.param("Ethernet1016", "1016,1017,1018,1019", "0", id="out-of-range-speed") + ] + ) + def test_add_remove_neg(self, testlog, port, lanes, speed): + # get port count + port_asicdb_count = len(self.dvs_port.get_port_ids(dbid=self.dvs_port.ASIC_DB)) + port_appdb_count = len(self.dvs_port.get_port_ids(dbid=self.dvs_port.APPL_DB)) + + # add port + self.dvs_port.create_port_generic(port, lanes, speed) + self.dvs_port.verify_port_count(port_appdb_count+1, self.dvs_port.APPL_DB) + self.dvs_port.verify_port_count(port_asicdb_count, self.dvs_port.ASIC_DB) + + # remove port + self.dvs_port.remove_port_generic(port) + self.dvs_port.verify_port_count(port_appdb_count, self.dvs_port.APPL_DB) + self.dvs_port.verify_port_count(port_asicdb_count, self.dvs_port.ASIC_DB) + + +@pytest.mark.usefixtures("dvs_port_manager") +class TestPortAddRemoveInvalidSerdesParam(object): + @pytest.fixture(scope="class") + def port_attr(self): + meta_dict = { + "port": "Ethernet1000", + "lanes": "1000,1001,1002,1003", + "speed": "100000", + "port_asicdb_count": len(self.dvs_port.get_port_ids(dbid=self.dvs_port.ASIC_DB)), + "port_appdb_count": len(self.dvs_port.get_port_ids(dbid=self.dvs_port.APPL_DB)) + } + yield meta_dict + + def verify_add_remove(self, attr, qualifiers): + # add port + self.dvs_port.create_port_generic(attr["port"], attr["lanes"], attr["speed"], qualifiers) + self.dvs_port.verify_port_count(attr["port_appdb_count"]+1, self.dvs_port.APPL_DB) + self.dvs_port.verify_port_count(attr["port_asicdb_count"], self.dvs_port.ASIC_DB) + + # remove port + self.dvs_port.remove_port_generic(attr["port"]) + self.dvs_port.verify_port_count(attr["port_appdb_count"], self.dvs_port.APPL_DB) + self.dvs_port.verify_port_count(attr["port_asicdb_count"], self.dvs_port.ASIC_DB) + + @pytest.mark.parametrize( + "serdes", [ + pytest.param("preemphasis", id="preemphasis"), + pytest.param("idriver", id="idriver"), + pytest.param("ipredriver", id="ipredriver"), + pytest.param("pre1", id="pre1"), + pytest.param("pre2", id="pre2"), + pytest.param("pre3", id="pre3"), + pytest.param("main", id="main"), + pytest.param("post1", id="post1"), + pytest.param("post2", id="post2"), + pytest.param("post3", id="post3"), + pytest.param("attn", id="attn") + ] + ) + def test_add_remove_neg(self, testlog, port_attr, serdes): + qualifiers = { serdes: "" } + self.verify_add_remove(port_attr, qualifiers) + + qualifiers = { serdes: "invalid" } + self.verify_add_remove(port_attr, qualifiers) + + +@pytest.mark.usefixtures("dvs_port_manager") +class TestPortAddRemoveInvalidParam(object): + def verify_add_remove(self, qualifiers): + port = "Ethernet1000" + lanes = "1000,1001,1002,1003" + speed = "100000" + + # get port count + port_asicdb_count = len(self.dvs_port.get_port_ids(dbid=self.dvs_port.ASIC_DB)) + port_appdb_count = len(self.dvs_port.get_port_ids(dbid=self.dvs_port.APPL_DB)) + + # add port + self.dvs_port.create_port_generic(port, lanes, speed, qualifiers) + self.dvs_port.verify_port_count(port_appdb_count+1, self.dvs_port.APPL_DB) + self.dvs_port.verify_port_count(port_asicdb_count, self.dvs_port.ASIC_DB) + + # remove port + self.dvs_port.remove_port_generic(port) + self.dvs_port.verify_port_count(port_appdb_count, self.dvs_port.APPL_DB) + self.dvs_port.verify_port_count(port_asicdb_count, self.dvs_port.ASIC_DB) + + def test_add_remove_neg_alias(self, testlog): + qualifiers = { "alias": "" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_index(self, testlog): + qualifiers = { "index": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "index": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_autoneg(self, testlog): + qualifiers = { "autoneg": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "autoneg": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_adv_speeds(self, testlog): + qualifiers = { "adv_speeds": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "adv_speeds": "0" } + self.verify_add_remove(qualifiers) + + qualifiers = { "adv_speeds": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_interface_type(self, testlog): + qualifiers = { "interface_type": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "interface_type": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_adv_interface_types(self, testlog): + qualifiers = { "adv_interface_types": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "adv_interface_types": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_fec(self, testlog): + qualifiers = { "fec": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "fec": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_mtu(self, testlog): + qualifiers = { "mtu": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "mtu": "0" } + self.verify_add_remove(qualifiers) + + qualifiers = { "mtu": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_tpid(self, testlog): + qualifiers = { "tpid": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "tpid": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_pfc_asym(self, testlog): + qualifiers = { "pfc_asym": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "pfc_asym": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_learn_mode(self, testlog): + qualifiers = { "learn_mode": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "learn_mode": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_link_training(self, testlog): + qualifiers = { "link_training": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "link_training": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_role(self, testlog): + qualifiers = { "role": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "role": "invalid" } + self.verify_add_remove(qualifiers) + + def test_add_remove_neg_admin_status(self, testlog): + qualifiers = { "admin_status": "" } + self.verify_add_remove(qualifiers) + + qualifiers = { "admin_status": "invalid" } + self.verify_add_remove(qualifiers)