diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index cd509b3bbd7..e7365f538d5 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -30,19 +30,27 @@ IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c } void IntfMgr::setIntfIp(const string &alias, const string &opCmd, - const string &ipPrefixStr, const bool ipv4) + const IpPrefix &ipPrefix) { - stringstream cmd; - string res; + stringstream cmd; + string res; + string ipPrefixStr = ipPrefix.to_string(); + string broadcastIpStr = ipPrefix.getBroadcastIp().to_string(); + int prefixLen = ipPrefix.getMaskLength(); - if (ipv4) + if (ipPrefix.isV4()) { - cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " dev " << alias; + (prefixLen < 31) ? + (cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " broadcast " << broadcastIpStr <<" dev " << alias) : + (cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " dev " << alias); } else { - cmd << IP_CMD << " -6 address " << opCmd << " " << ipPrefixStr << " dev " << alias; + (prefixLen < 127) ? + (cmd << IP_CMD << " -6 address " << opCmd << " " << ipPrefixStr << " broadcast " << broadcastIpStr << " dev " << alias) : + (cmd << IP_CMD << " -6 address " << opCmd << " " << ipPrefixStr << " dev " << alias); } + int ret = swss::exec(cmd.str(), res); if (ret) { @@ -202,7 +210,7 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, // Set Interface IP except for lo if (!is_lo) { - setIntfIp(alias, "add", ip_prefix.to_string(), ip_prefix.isV4()); + setIntfIp(alias, "add", ip_prefix); } std::vector fvVector; @@ -219,7 +227,7 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, // Set Interface IP except for lo if (!is_lo) { - setIntfIp(alias, "del", ip_prefix.to_string(), ip_prefix.isV4()); + setIntfIp(alias, "del", ip_prefix); } m_appIntfTableProducer.del(appKey); m_stateIntfTable.del(keys[0] + state_db_key_delimiter + keys[1]); diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index d10b5d8b4c4..a250ff65fbf 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -21,7 +21,7 @@ class IntfMgr : public Orch Table m_cfgIntfTable, m_cfgVlanIntfTable; Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable; - void setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr, const bool ipv4 = true); + void setIntfIp(const string &alias, const string &opCmd, const IpPrefix &ipPrefix); void setIntfVrf(const string &alias, const string vrfName); bool doIntfGeneralTask(const vector& keys, const vector& data, const string& op); bool doIntfAddrTask(const vector& keys, const vector& data, const string& op); diff --git a/cfgmgr/nbrmgr.cpp b/cfgmgr/nbrmgr.cpp index d2ca7ebc96d..e3080b7c01a 100644 --- a/cfgmgr/nbrmgr.cpp +++ b/cfgmgr/nbrmgr.cpp @@ -47,7 +47,8 @@ NbrMgr::NbrMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, con m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), - m_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME) + m_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME), + m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME) { int err = 0; @@ -91,6 +92,19 @@ bool NbrMgr::isIntfStateOk(const string &alias) return false; } +bool NbrMgr::isNeighRestoreDone() +{ + string value; + + m_stateNeighRestoreTable.hget("Flags", "restored", value); + if (value == "true") + { + SWSS_LOG_INFO("Kernel neighbor table restore is done"); + return true; + } + return false; +} + bool NbrMgr::setNeighbor(const string& alias, const IpAddress& ip, const MacAddress& mac) { SWSS_LOG_ENTER(); diff --git a/cfgmgr/nbrmgr.h b/cfgmgr/nbrmgr.h index 61fba1bf7b2..24d83971a1f 100644 --- a/cfgmgr/nbrmgr.h +++ b/cfgmgr/nbrmgr.h @@ -20,13 +20,15 @@ class NbrMgr : public Orch NbrMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames); using Orch::doTask; + bool isNeighRestoreDone(); + private: bool isIntfStateOk(const string &alias); bool setNeighbor(const string& alias, const IpAddress& ip, const MacAddress& mac); void doTask(Consumer &consumer); - Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateIntfTable; + Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateIntfTable, m_stateNeighRestoreTable; struct nl_sock *m_nl_sock; }; diff --git a/cfgmgr/nbrmgrd.cpp b/cfgmgr/nbrmgrd.cpp index 2872fa9e679..fc2ed2158d3 100644 --- a/cfgmgr/nbrmgrd.cpp +++ b/cfgmgr/nbrmgrd.cpp @@ -3,15 +3,20 @@ #include #include #include +#include #include "select.h" #include "exec.h" #include "schema.h" #include "nbrmgr.h" +#include "warm_restart.h" using namespace std; using namespace swss; +#define RESTORE_NEIGH_WAIT_TIME_OUT 120 +#define RESTORE_NEIGH_WAIT_TIME_INT 10 + /* select() function timeout retry time, in millisecond */ #define SELECT_TIMEOUT 1000 @@ -50,6 +55,27 @@ int main(int argc, char **argv) NbrMgr nbrmgr(&cfgDb, &appDb, &stateDb, cfg_nbr_tables); + WarmStart::initialize("nbrmgrd", "swss"); + WarmStart::checkWarmStart("nbrmgrd", "swss"); + + if (WarmStart::isWarmStart()) + { + chrono::steady_clock::time_point starttime = chrono::steady_clock::now(); + while (!nbrmgr.isNeighRestoreDone()) + { + chrono::duration time_span = chrono::duration_cast> + (chrono::steady_clock::now() - starttime); + int pasttime = int(time_span.count()); + SWSS_LOG_INFO("Kernel neighbor table restoration waited for %d seconds", pasttime); + if (pasttime > RESTORE_NEIGH_WAIT_TIME_OUT) + { + SWSS_LOG_WARN("Kernel neighbor table restore is not finished!"); + break; + } + sleep(RESTORE_NEIGH_WAIT_TIME_INT); + } + } + std::vector cfgOrchList = {&nbrmgr}; swss::Select s; diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index 3d39488aaae..fc5949252e2 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -51,13 +51,19 @@ VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c // The command should be generated as: // /bin/bash -c "/sbin/ip link del Bridge 2>/dev/null ; // /sbin/ip link add Bridge up type bridge && - // /sbin/bridge vlan del vid 1 dev Bridge self" + // /sbin/bridge vlan del vid 1 dev Bridge self; + // /sbin/ip link del dummy 2>/dev/null; + // /sbin/ip link add dummy type dummy && + // sbin/ip link set dummy master Bridge" const std::string cmds = std::string("") + BASH_CMD + " -c \"" + IP_CMD + " link del " + DOT1Q_BRIDGE_NAME + " 2>/dev/null; " + IP_CMD + " link add " + DOT1Q_BRIDGE_NAME + " up type bridge && " - + BRIDGE_CMD + " vlan del vid " + DEFAULT_VLAN_ID + " dev " + DOT1Q_BRIDGE_NAME + " self\""; + + BRIDGE_CMD + " vlan del vid " + DEFAULT_VLAN_ID + " dev " + DOT1Q_BRIDGE_NAME + " self; " + + IP_CMD + " link del dev dummy 2>/dev/null; " + + IP_CMD + " link add dummy type dummy && " + + IP_CMD + " link set dummy master " + DOT1Q_BRIDGE_NAME + "\""; std::string res; EXEC_WITH_ERROR_THROW(cmds, res); @@ -169,10 +175,12 @@ bool VlanMgr::addHostVlanMember(int vlan_id, const string &port_alias, const str // The command should be generated as: // /bin/bash -c "/sbin/ip link set {{port_alias}} master Bridge && + // /sbin/bridge vlan del vid 1 dev {{ port_alias }} && // /sbin/bridge vlan add vid {{vlan_id}} dev {{port_alias}} {{tagging_mode}}" const std::string cmds = std::string("") + BASH_CMD + " -c \"" + IP_CMD + " link set " + port_alias + " master " + DOT1Q_BRIDGE_NAME + " && " + + BRIDGE_CMD + " vlan del vid " + DEFAULT_VLAN_ID + " dev " + port_alias + " && " + BRIDGE_CMD + " vlan add vid " + std::to_string(vlan_id) + " dev " + port_alias + " " + tagging_cmd + "\""; std::string res; diff --git a/debian/swss.install b/debian/swss.install index 23f9b18b8a6..7dd28196ac7 100644 --- a/debian/swss.install +++ b/debian/swss.install @@ -1,3 +1,4 @@ swssconfig/sample/netbouncer.json etc/swss/config.d swssconfig/sample/00-copp.config.json etc/swss/config.d neighsyncd/restore_neighbors.py usr/bin +fpmsyncd/bgp_eoiu_marker.py usr/bin diff --git a/doc/Configuration.md b/doc/Configuration.md new file mode 100644 index 00000000000..26a67da0c0d --- /dev/null +++ b/doc/Configuration.md @@ -0,0 +1,1165 @@ +# SONiC Configuration Database Manual + +Table of Contents +================= + + * [Introduction](#introduction) + * [Configuration](#configuration) + * [Config Load and Save](#config-load-and-save) + * [Incremental Configuration](#incremental-configuration) + * [Redis and Json Schema](#redis-and-json-schema) + * [ACL and Mirroring](#acl-and-mirroring) + * [BGP Sessions](#bgp-sessions) + * [BUFFER_PG](#buffer_pg) + * [Buffer pool](#buffer-pool) + * [Buffer profile](#buffer-profile) + * [Buffer queue](#buffer-queue) + * [Cable length](#cable-length) + * [COPP_TABLE](#copp_table) + * [CRM](#crm) + * [Data Plane L3 Interfaces](#data-plane-l3-interfaces) + * [Device Metadata](#device-metadata) + * [Device neighbor metada](#device-neighbor-metada) + * [DSCP_TO_TC_MAP](#dscp_to_tc_map) + * [FLEX_COUNTER_TABLE](#flex_counter_table) + * [L2 Neighbors](#l2-neighbors) + * [Loopback Interface](#loopback-interface) + * [Management Interface](#management-interface) + * [Management port](#management-port) + * [Management VRF](#management-vrf) + * [MAP_PFC_PRIORITY_TO_QUEUE](#map_pfc_priority_to_queue) + * [NTP and SYSLOG servers](#ntp-and-syslog-servers) + * [Port](#port) + * [Port Channel](#port-channel) + * [Portchannel member](#portchannel-member) + * [Port QoS Map](#port-qos-map) + * [Queue](#queue) + * [Tacplus Server](#tacplus-server) + * [TC to Priority group map](#tc-to-priority-group-map) + * [TC to Queue map](#tc-to-queue-map) + * [Versions](#versions) + * [VLAN](#vlan) + * [VLAN_MEMBER](#vlan_member) + * [Virtual router](#virtual-router) + * [WRED_PROFILE](#wred_profile) + * [For Developers](#for-developers) + * [Generating Application Config by Jinja2 Template](#generating-application-config-by-jinja2-template) + * [Incremental Configuration by Subscribing to ConfigDB](#incremental-configuration-by-subscribing-to-configdb) + + + +# Introduction +This document lists the configuration commands schema applied in the SONiC eco system. All these commands find relevance in collecting system information, analysis and even for trouble shooting. All the commands are categorized under relevant topics with corresponding examples. + +# Configuration + +SONiC is managing configuration in a single source of truth - a redisDB +instance that we refer as ConfigDB. Applications subscribe to ConfigDB +and generate their running configuration correspondingly. + +(Before Sep 2017, we were using an XML file named minigraph.xml to +configure SONiC devices. For historical documentation, please refer to +[Configuration with +Minigraph](https://github.com/Azure/SONiC/wiki/Configuration-with-Minigraph-(~Sep-2017))) + +# **Config Load and Save** + +In current version of SONiC, ConfigDB is implemented as database 4 of +local redis. When system boots, configurations will be loaded from +/etc/sonic/config_db.json file into redis. Please note that ConfigDB +content won't be written back into /etc/sonic/config_db.json file +automatically. In order to do that, a config save command need to be +manually executed from CLI. Similarly, config load will trigger a force +load of json file into DB. Generally, content in +/etc/sonic/config_db.json can be considered as starting config, and +content in redisDB running config. + +We keep a way to load configuration from minigraph and write into +ConfigDB for backward compatibility. To do that, run `config +load_minigraph`. + +### Incremental Configuration + +The design of ConfigDB supports incremental configuration - application +could subscribe to changes in ConfigDB and response correspondingly. +However, this feature is not implemented by all applications yet. By Sep +2017 now, the only application that supports incremental configuration +is BGP (docker-fpm-quagga). For other applications, a manual restart is +required after configuration changes in ConfigDB. + +# **Redis and Json Schema** + +ConfigDB uses a table-object schema that is similar with +[AppDB](https://github.com/Azure/sonic-swss/blob/4c56d23b9ff4940bdf576cf7c9e5aa77adcbbdcc/doc/swss-schema.md), +and `config_db.json` is a straight-forward serialization of DB. As an +example, the following fragments could be BGP-related configuration in +redis and json, correspondingly: + + +***Redis format*** +``` +127.0.0.1:6379[4]> keys BGP_NEIGHBOR:* + +1) "BGP_NEIGHBOR:10.0.0.31" +2) "BGP_NEIGHBOR:10.0.0.39" +3) "BGP_NEIGHBOR:10.0.0.11" +4) "BGP_NEIGHBOR:10.0.0.7" + +... + +127.0.0.1:6379[4]> hgetall BGP_NEIGHBOR:10.0.0.3 + +1) "admin_status" +2) "up" +3) "peer_addr" +4) "10.0.0.2" +5) "asn" +6) "65200" +7) "name" +8) "ARISTA07T2" +``` + +***Json format*** +``` +"BGP_NEIGHBOR": { + "10.0.0.57": { + "rrclient": "0", + "name": "ARISTA01T1", + "local_addr": "10.0.0.56", + "nhopself": "0", + "holdtime": "10", + "asn": "64600", + "keepalive": "3" + }, + "10.0.0.59": { + "rrclient": "0", + "name": "ARISTA02T1", + "local_addr": "10.0.0.58", + "nhopself": "0", + "holdtime": "10", + "asn": "64600", + "keepalive": "3" + }, +} +``` + +Full sample config_db.json files are availables at +[here](https://github.com/Azure/SONiC/blob/gh-pages/doc/config_db.json) +and +[here](https://github.com/Azure/SONiC/blob/gh-pages/doc/config_db_t0.json). + + +### ACL and Mirroring + +ACL and mirroring related configuration are defined in +**MIRROR_SESSION**, **ACL_TABLE** and **ACL_RULE** tables. Those +tables are in progress of migrating from APPDB. Please refer to their +schema in APPDB +[here](https://github.com/Azure/sonic-swss/blob/4c56d23b9ff4940bdf576cf7c9e5aa77adcbbdcc/doc/swss-schema.md) +and migration plan +[here](https://github.com/Azure/SONiC/wiki/ACL-Configuration-Requirement-Description). + +``` +{ +"MIRROR_SESSION": { + "everflow0": { + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2" + } + }, + +"ACL_TABLE": { + "DATAACL": { + "policy_desc" : "data_acl", + "type": "l3", + "ports": [ + "Ethernet0", + "Ethernet4", + "Ethernet8", + "Ethernet12" + ] + } + } +} +``` + +***Below ACL table added as per the mail*** +``` +{ +"ACL_TABLE": { + "aaa": { + "type": "L3", + "ports": "Ethernet0" + } + }, +"ACL_RULE": { + "aaa|rule_0": { + "PRIORITY": "55", + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "0" + }, + "aaa|rule_1": { + "PRIORITY": "55", + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "1" + } + } +} +``` + +***Below ACL table added by comparig minigraph.xml & config_db.json*** + +``` +{ +"ACL_TABLE": { + "EVERFLOW": { + "type": "MIRROR", + "policy_desc": "EVERFLOW", + "ports": [ + "PortChannel0001", + "PortChannel0002", + "PortChannel0003", + "PortChannel0004" + ] + }, + "EVERFLOWV6": { + "type": "MIRRORV6", + "policy_desc": "EVERFLOWV6", + "ports": [ + "PortChannel0001", + "PortChannel0002", + "PortChannel0003", + "PortChannel0004" + ] + }, + "SNMP_ACL": { + "services": [ + "SNMP" + ], + "type": "CTRLPLANE", + "policy_desc": "SNMP_ACL" + }, + "SSH_ONLY": { + "services": [ + "SSH" + ], + "type": "CTRLPLANE", + "policy_desc": "SSH_ONLY" + } + }, + +"ACL_RULE": { + "SNMP_ACL|DEFAULT_RULE": { + "PRIORITY": "1", + "PACKET_ACTION": "DROP", + "ETHER_TYPE": "2048" + }, + "SNMP_ACL|RULE_1": { + "PRIORITY": "9999", + "PACKET_ACTION": "ACCEPT", + "SRC_IP": "1.1.1.1/32", + "IP_PROTOCOL": "17" + }, + "SNMP_ACL|RULE_2": { + "PRIORITY": "9998", + "PACKET_ACTION": "ACCEPT", + "SRC_IP": "2.2.2.2/32", + "IP_PROTOCOL": "17" + }, + "SSH_ONLY|DEFAULT_RULE": { + "PRIORITY": "1", + "PACKET_ACTION": "DROP", + "ETHER_TYPE": "2048" + }, + "SSH_ONLY|RULE_1": { + "PRIORITY": "9999", + "PACKET_ACTION": "ACCEPT", + "SRC_IP": "4.4.4.4/8", + "IP_PROTOCOL": "6" + } + } +} + +``` + +### BGP Sessions + +BGP session configuration is defined in **BGP_NEIGHBOR** table. BGP +neighbor address is used as key of bgp neighbor objects. Object +attributes include remote AS number, neighbor router name, and local +peering address. Dynamic neighbor is also supported by defining peer +group name and IP ranges in **BGP_PEER_RANGE** table. + +``` +{ +"BGP_NEIGHBOR": { + "10.0.0.61": { + "local_addr": "10.0.0.60", + "asn": 64015, + "name": "ARISTA15T0" + }, + "10.0.0.49": { + "local_addr": "10.0.0.48", + "asn": 64009, + "name": "ARISTA09T0" + }, + + "10.0.0.63": { + "rrclient": "0", + "name": "ARISTA04T1", + "local_addr": "10.0.0.62", + "nhopself": "0", + "holdtime": "10", + "asn": "64600", + "keepalive": "3" + } + +"BGP_PEER_RANGE": { + "BGPSLBPassive": { + "name": "BGPSLBPassive", + "ip_range": [ + "10.250.0.0/27" + ] + }, + "BGPVac": { + "name": "BGPVac", + "ip_range": [ + "10.2.0.0/16" + ] + } + } +} +``` + +### BUFFER_PG + +``` +{ +"BUFFER_PG": { + "Ethernet0|3-4": { + "profile": "[BUFFER_PROFILE|pg_lossless_40000_5m_profile]" + }, + "Ethernet1|3-4": { + "profile": "[BUFFER_PROFILE|pg_lossless_40000_5m_profile]" + }, + "Ethernet2|3-4": { + "profile": "[BUFFER_PROFILE|pg_lossless_40000_5m_profile]" + } + } +} + +``` + +### Buffer pool + +``` +{ +"BUFFER_POOL": { + "egress_lossless_pool": { + "type": "egress", + "mode": "static", + "size": "15982720" + }, + "egress_lossy_pool": { + "type": "egress", + "mode": "dynamic", + "size": "9243812" + }, + "ingress_lossless_pool": { + "xoff": "4194112", + "type": "ingress", + "mode": "dynamic", + "size": "10875072" + } + } +} + +``` + + +### Buffer profile + +``` +{ +"BUFFER_PROFILE": { + "egress_lossless_profile": { + "static_th": "3995680", + "pool": "[BUFFER_POOL|egress_lossless_pool]", + "size": "1518" + }, + "egress_lossy_profile": { + "dynamic_th": "3", + "pool": "[BUFFER_POOL|egress_lossy_pool]", + "size": "1518" + }, + "ingress_lossy_profile": { + "dynamic_th": "3", + "pool": "[BUFFER_POOL|ingress_lossless_pool]", + "size": "0" + }, + "pg_lossless_40000_5m_profile": { + "xon_offset": "2288", + "dynamic_th": "-3", + "xon": "2288", + "xoff": "66560", + "pool": "[BUFFER_POOL|ingress_lossless_pool]", + "size": "1248" + }, + "pg_lossless_40000_40m_profile": { + "xon_offset": "2288", + "dynamic_th": "-3", + "xon": "2288", + "xoff": "71552", + "pool": "[BUFFER_POOL|ingress_lossless_pool]", + "size": "1248" + } + } +} + +``` + + +### Buffer queue + +``` +{ +"BUFFER_QUEUE": { + "Ethernet50,Ethernet52,Ethernet54,Ethernet56|0-2": { + "profile": "[BUFFER_PROFILE|egress_lossy_profile]" + }, + "Ethernet50,Ethernet52,Ethernet54,Ethernet56|3-4": { + "profile": "[BUFFER_PROFILE|egress_lossless_profile]" + }, + "Ethernet50,Ethernet52,Ethernet54,Ethernet56|5-6": { + "profile": "[BUFFER_PROFILE|egress_lossy_profile]" + } + } +} + +``` + + +### Cable length + +``` +{ +"CABLE_LENGTH": { + "AZURE": { + "Ethernet8": "5m", + "Ethernet9": "5m", + "Ethernet2": "5m", + "Ethernet58": "5m", + "Ethernet59": "5m", + "Ethernet50": "40m", + "Ethernet51": "5m", + "Ethernet52": "40m", + "Ethernet53": "5m", + "Ethernet54": "40m", + "Ethernet55": "5m", + "Ethernet56": "40m" + } + } +} + +``` + +### COPP_TABLE + +``` +{ +"COPP_TABLE": { + "default": { + "cbs": "600", + "cir": "600", + "meter_type": "packets", + "mode": "sr_tcm", + "queue": "0", + "red_action": "drop" + }, + + "trap.group.arp": { + "cbs": "600", + "cir": "600", + "meter_type": "packets", + "mode": "sr_tcm", + "queue": "4", + "red_action": "drop", + "trap_action": "trap", + "trap_ids": "arp_req,arp_resp,neigh_discovery", + "trap_priority": "4" + }, + + "trap.group.lldp.dhcp.udld": { + "queue": "4", + "trap_action": "trap", + "trap_ids": "lldp,dhcp,udld", + "trap_priority": "4" + }, + + "trap.group.bgp.lacp": { + "queue": "4", + "trap_action": "trap", + "trap_ids": "bgp,bgpv6,lacp", + "trap_priority": "4" + }, + + "trap.group.ip2me": { + "cbs": "600", + "cir": "600", + "meter_type": "packets", + "mode": "sr_tcm", + "queue": "1", + "red_action": "drop", + "trap_action": "trap", + "trap_ids": "ip2me", + "trap_priority": "1" + } + } +} +``` + +### CRM + +``` +{ +"CRM": { + "Config": { + "acl_table_threshold_type": "percentage", + "nexthop_group_threshold_type": "percentage", + "fdb_entry_high_threshold": "85", + "acl_entry_threshold_type": "percentage", + "ipv6_neighbor_low_threshold": "70", + "nexthop_group_member_low_threshold": "70", + "acl_group_high_threshold": "85", + "ipv4_route_high_threshold": "85", + "acl_counter_high_threshold": "85", + "ipv4_route_low_threshold": "70", + "ipv4_route_threshold_type": "percentage", + "ipv4_neighbor_low_threshold": "70", + "acl_group_threshold_type": "percentage", + "ipv4_nexthop_high_threshold": "85", + "ipv6_route_threshold_type": "percentage" + } + } +} + +``` + +### Data Plane L3 Interfaces + +IP configuration for data plane are defined in **INTERFACE**, +**PORTCHANNEL_INTERFACE**, and **VLAN_INTERFACE** table. The objects +in all three tables have the interface (could be physical port, port +channel, or vlan) that IP address is attached to as first-level key, and +IP prefix as second-level key. IP interface objects don't have any +attributes. + +``` +{ +"INTERFACE": { + "Ethernet0|10.0.0.0/31": {}, + "Ethernet4|10.0.0.2/31": {}, + "Ethernet8|10.0.0.4/31": {} + ... + }, + +"PORTCHANNEL_INTERFACE": { + "PortChannel01|10.0.0.56/31": {}, + "PortChannel01|FC00::71/126": {}, + "PortChannel02|10.0.0.58/31": {}, + "PortChannel02|FC00::75/126": {} + ... + }, +"VLAN_INTERFACE": { + "Vlan1000|192.168.0.1/27": {} + } +} + +``` + + +### Device Metadata + +The **DEVICE_METADATA** table contains only one object named +*localhost*. In this table the device metadata such as hostname, hwsku, +deployment envionment id and deployment type are specified. BGP local AS +number is also specified in this table as current only single BGP +instance is supported in SONiC. + +``` +{ +"DEVICE_METADATA": { + "localhost": { + "hwsku": "Force10-S6100", + "default_bgp_status": "up", + "docker_routing_config_mode": "unified", + "hostname": "sonic-s6100-01", + "platform": "x86_64-dell_s6100_c2538-r0", + "mac": "4c:76:25:f4:70:82", + "default_pfcwd_status": "disable", + "bgp_asn": "65100", + "deployment_id": "1", + "type": "ToRRouter" + } + } +} + +``` + + +### Device neighbor metada + +``` +{ +"DEVICE_NEIGHBOR_METADATA": { + "ARISTA01T1": { + "lo_addr": "None", + "mgmt_addr": "10.11.150.45", + "hwsku": "Arista-VM", + "type": "LeafRouter" + }, + "ARISTA02T1": { + "lo_addr": "None", + "mgmt_addr": "10.11.150.46", + "hwsku": "Arista-VM", + "type": "LeafRouter" + } + } +} + +``` + + +### DSCP_TO_TC_MAP +``` +{ +"DSCP_TO_TC_MAP": { + "AZURE": { + "1": "1", + "0": "1", + "3": "3", + "2": "1", + "5": "2", + "4": "4", + "7": "1", + "6": "1", + "9": "1", + "8": "0" + } + } +} + +``` + +### FLEX_COUNTER_TABLE + +``` +{ +"FLEX_COUNTER_TABLE": { + "PFCWD": { + "FLEX_COUNTER_STATUS": "enable" + }, + "PORT": { + "FLEX_COUNTER_STATUS": "enable" + }, + "QUEUE": { + "FLEX_COUNTER_STATUS": "enable" + } + } +} + +``` + + +### L2 Neighbors + +The L2 neighbor and connection information can be configured in +**DEVICE_NEIGHBOR** table. Those information are used mainly for LLDP. +While mandatory fields include neighbor name acting as object key and +remote port / local port information in attributes, optional information +about neighbor device such as device type, hwsku, management address and +loopback address can also be defined. + +``` +{ +"DEVICE_NEIGHBOR": { + "ARISTA04T1": { + "mgmt_addr": "10.20.0.163", + "hwsku": "Arista", + "lo_addr": null, + "local_port": "Ethernet124", + "type": "LeafRouter", + "port": "Ethernet1" + }, + "ARISTA03T1": { + "mgmt_addr": "10.20.0.162", + "hwsku": "Arista", + "lo_addr": null, + "local_port": "Ethernet120", + "type": "LeafRouter", + "port": "Ethernet1" + }, + "ARISTA02T1": { + "mgmt_addr": "10.20.0.161", + "hwsku": "Arista", + "lo_addr": null, + "local_port": "Ethernet116", + "type": "LeafRouter", + "port": "Ethernet1" + }, + "ARISTA01T1": { + "mgmt_addr": "10.20.0.160", + "hwsku": "Arista", + "lo_addr": null, + "local_port": "Ethernet112", + "type": "LeafRouter", + "port": "Ethernet1" + } + } +} +``` + +### Loopback Interface + +Loopback interface configuration lies in **LOOPBACK_INTERFACE** table +and has similar schema with data plane interfaces. The loopback device +name and loopback IP prefix act as multi-level key for loopback +interface objects. + +``` +{ +"LOOPBACK_INTERFACE": { + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {} + } +} + +``` + +### Management Interface + +Management interfaces are defined in **MGMT_INTERFACE** table. Object +key is composed of management interface name and IP prefix. Attribute +***gwaddr*** specify the gateway address of the prefix. +***forced_mgmt_routes*** attribute can be used to specify addresses / +prefixes traffic to which are forced to go through management network +instead of data network. + +``` +{ +"MGMT_INTERFACE": { + "eth0|10.11.150.11/16": { + "gwaddr": "10.11.0.1" + }, + "eth0|FC00:2::32/64": { + "forced_mgmt_routes": [ + "10.0.0.100/31", + "10.250.0.8", + "10.255.0.0/28" + ], + "gwaddr": "fc00:2::1" + } + } +} + +``` + +### Management port + +``` +{ +"MGMT_PORT": { + "eth0": { + "alias": "eth0", + "admin_status": "up" + } + } +} + +``` + + +### Management VRF + +``` +{ +"MGMT_VRF_CONFIG": { + "vrf_global": { + "mgmtVrfEnabled": "true" + } + } +} +``` + +### MAP_PFC_PRIORITY_TO_QUEUE + +``` +{ +"MAP_PFC_PRIORITY_TO_QUEUE": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "2": "2", + "5": "5", + "4": "4", + "7": "7", + "6": "6" + } + } +} +``` + + +### NTP and SYSLOG servers + +These information are configured in individual tables. Domain name or IP +address of the server is used as object key. Currently there are no +attributes in those objects. + +***NTP server*** +``` +{ +"NTP_SERVER": { + "2.debian.pool.ntp.org": {}, + "1.debian.pool.ntp.org": {}, + "3.debian.pool.ntp.org": {}, + "0.debian.pool.ntp.org": {} + }, + +"NTP_SERVER": { + "23.92.29.245": {}, + "204.2.134.164": {} + } +} +``` + +***Syslogserver*** +``` +{ +"SYSLOG_SERVER": { + "10.0.0.5": {}, + "10.0.0.6": {}, + "10.11.150.5": {} + } +} +``` + +### Port + +In this table the physical port configurations are defined. Each object +will have port name as its key, and port name alias and port speed as +optional attributes. + +``` +{ +"PORT": { + "Ethernet0": { + "index": "0", + "lanes": "101,102", + "description": "fortyGigE1/1/1", + "mtu": "9100", + "alias": "fortyGigE1/1/1", + "speed": "40000" + }, + "Ethernet1": { + "index": "1", + "lanes": "103,104", + "description": "fortyGigE1/1/2", + "mtu": "9100", + "alias": "fortyGigE1/1/2", + "admin_status": "up", + "speed": "40000" + }, + "Ethernet63": { + "index": "63", + "lanes": "87,88", + "description": "fortyGigE1/4/16", + "mtu": "9100", + "alias": "fortyGigE1/4/16", + "speed": "40000" + } + } +} + +``` + +### Port Channel + +Port channels are defined in **PORTCHANNEL** table with port channel +name as object key and member list as attribute. + +``` +{ +"PORTCHANNEL": { + "PortChannel0003": { + "admin_status": "up", + "min_links": "1", + "members": [ + "Ethernet54" + ], + "mtu": "9100" + }, + "PortChannel0004": { + "admin_status": "up", + "min_links": "1", + "members": [ + "Ethernet56" + ], + "mtu": "9100" + } + } +} +``` + + +### Portchannel member + +``` +{ +"PORTCHANNEL_MEMBER": { + "PortChannel0001|Ethernet50": {}, + "PortChannel0002|Ethernet52": {}, + "PortChannel0003|Ethernet54": {}, + "PortChannel0004|Ethernet56": {} + } +} +``` + +### Port QoS Map + +``` +{ +"PORT_QOS_MAP": { + "Ethernet50,Ethernet52,Ethernet54,Ethernet56": { + "tc_to_pg_map": "[TC_TO_PRIORITY_GROUP_MAP|AZURE]", + "tc_to_queue_map": "[TC_TO_QUEUE_MAP|AZURE]", + "pfc_enable": "3,4", + "pfc_to_queue_map": "[MAP_PFC_PRIORITY_TO_QUEUE|AZURE]", + "dscp_to_tc_map": "[DSCP_TO_TC_MAP|AZURE]" + } + } +} +``` + +### Queue +``` +{ +"QUEUE": { + "Ethernet56|4": { + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]", + "scheduler": "[SCHEDULER|scheduler.1]" + }, + "Ethernet56|5": { + "scheduler": "[SCHEDULER|scheduler.0]" + }, + "Ethernet56|6": { + "scheduler": "[SCHEDULER|scheduler.0]" + } + } +} +``` + + +### Tacplus Server + +``` +{ +"TACPLUS_SERVER": { + "10.0.0.8": { + "priority": "1", + "tcp_port": "49" + }, + "10.0.0.9": { + "priority": "1", + "tcp_port": "49" + } + } +} +``` + + +### TC to Priority group map + +``` +{ +"TC_TO_PRIORITY_GROUP_MAP": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "2": "2", + "5": "5", + "4": "4", + "7": "7", + "6": "6" + } + } +} +``` + +### TC to Queue map + +``` +{ +"TC_TO_QUEUE_MAP": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "2": "2", + "5": "5", + "4": "4", + "7": "7", + "6": "6" + } + } +} +``` + +### Versions + +This table is where the curret version of the software is recorded. +``` +{ + "VERSIONS": { + "DATABASE": { + "VERSION": "version_1_0_1" + } + } +} +``` + +### VLAN + +This table is where VLANs are defined. VLAN name is used as object key, +and member list as well as an integer id are defined as attributes. If a +DHCP relay is required for this VLAN, a dhcp_servers attribute must be +specified for that VLAN, the value of which is a list that must contain +the domain name or IP address of one or more DHCP servers. + +``` +{ +"VLAN": { + "Vlan1000": { + "dhcp_servers": [ + "192.0.0.1", + "192.0.0.2", + "192.0.0.3", + "192.0.0.4" + ], + "members": [ + "Ethernet0", + "Ethernet4", + "Ethernet8", + "Ethernet12" + ], + "vlanid": "1000" + } + } +} +``` + +### VLAN_MEMBER + +VLAN member table has Vlan name together with physical port or port +channel name as object key, and tagging mode as attributes. + +``` +{ +"VLAN_MEMBER": { + "Vlan1000|PortChannel47": { + "tagging_mode": "untagged" + }, + "Vlan1000|Ethernet8": { + "tagging_mode": "untagged" + }, + "Vlan2000|PortChannel47": { + "tagging_mode": "tagged" + } + } +} +``` + +### Virtual router + +The virtual router table allows to insert or update a new virtual router +instance. The key of the instance is its name. The attributes in the +table allow to change properties of a virtual router. Attributes: + +- 'v4' contains boolean value 'true' or 'false'. Enable or + disable IPv4 in the virtual router +- 'v6' contains boolean value 'true' or 'false'. Enable or + disable IPv6 in the virtual router +- 'src_mac' contains MAC address. What source MAC address will be + used for packets egressing from the virtual router +- 'ttl_action' contains packet action. Defines the action for + packets with TTL == 0 or TTL == 1 +- 'ip_opt_action' contains packet action. Defines the action for + packets with IP options +- 'l3_mc_action' contains packet action. Defines the action for + unknown L3 multicast packets + +The packet action could be: + +- 'drop' +- 'forward' +- 'copy' +- 'copy_cancel' +- 'trap' +- 'log' +- 'deny' +- 'transit' + + +***TBD*** +``` +'VRF:rid1': { + 'v4': 'true', + 'v6': 'false', + 'src_mac': '02:04:05:06:07:08', + 'ttl_action': 'copy', + 'ip_opt_action': 'deny', + 'l3_mc_action': 'drop' +} +``` + + +### WRED_PROFILE + +``` +{ +"WRED_PROFILE": { + "AZURE_LOSSLESS": { + "red_max_threshold": "2097152", + "wred_green_enable": "true", + "ecn": "ecn_all", + "green_min_threshold": "1048576", + "red_min_threshold": "1048576", + "wred_yellow_enable": "true", + "yellow_min_threshold": "1048576", + "green_max_threshold": "2097152", + "green_drop_probability": "5", + "yellow_max_threshold": "2097152", + "wred_red_enable": "true", + "yellow_drop_probability": "5", + "red_drop_probability": "5" + } + } +} +``` + +For Developers +============== + +Generating Application Config by Jinja2 Template +------------------------------------------------ + +To be added. + +Incremental Configuration by Subscribing to ConfigDB +---------------------------------------------------- + +Detail instruction to be added. A sample could be found in this +[PR](https://github.com/Azure/sonic-buildimage/pull/861) that +implemented dynamic configuration for BGP. \ No newline at end of file diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 7d934b876b8..ac7e25ce5ec 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -459,7 +459,17 @@ Stores rules associated with a specific ACL table on the switch. : next-hop ip address Example: "10.0.0.1" : next-hop group set of addresses Example: "10.0.0.1,10.0.0.3" - mirror_action = 1*255VCHAR ; refer to the mirror session + redirect_action = 1*255CHAR ; redirect parameter + ; This parameter defines a destination for redirected packets + ; it could be: + : name of physical port. Example: "Ethernet10" + : name of LAG port Example: "PortChannel5" + : next-hop ip address Example: "10.0.0.1" + : next-hop group set of addresses Example: "10.0.0.1,10.0.0.3" + + mirror_action = 1*255VCHAR ; refer to the mirror session (by default it will be ingress mirror action) + mirror_ingress_action = 1*255VCHAR ; refer to the mirror session + mirror_egress_action = 1*255VCHAR ; refer to the mirror session ether_type = h16 ; Ethernet type field @@ -809,7 +819,7 @@ Stores information for physical switch ports managed by the switch chip. Ports t ;Stores application and orchdameon warm start status ;Status: work in progress - key = WARM_RESTART_TABLE:process_name ; process_name is a unique process identifier. + key = WARM_RESTART_TABLE|process_name ; process_name is a unique process identifier. ; with exception of 'warm-shutdown' operation. ; 'warm-shutdown' operation key is used to ; track warm shutdown stages and results. @@ -839,6 +849,33 @@ Stores information for physical switch ports managed by the switch chip. Ports t key = NEIGH_RESTORE_TABLE|Flags restored = "true" / "false" ; restored state +### BGP\_STATE\_TABLE + ;Stores bgp status + ;Status: work in progress + + key = BGP_STATE_TABLE|family|eoiu ; family = "IPv4" / "IPv6" ; address family. + + state = "unknown" / "reached" / "consumed" ; unknown: eoiu state not fetched yet. + ; reached: bgp eoiu done. + ; + ; consumed: the reached state has been consumed by application. + timestamp = time-stamp ; "%Y-%m-%d %H:%M:%S", full-date and partial-time separated by + ; white space. Example: 2019-04-25 09:39:19 + + ;value annotations + date-fullyear = 4DIGIT + date-month = 2DIGIT ; 01-12 + date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + ; month/year + time-hour = 2DIGIT ; 00-23 + time-minute = 2DIGIT ; 00-59 + time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second + ; rules + + partial-time = time-hour ":" time-minute ":" time-second + full-date = date-fullyear "-" date-month "-" date-mday + time-stamp = full-date %x20 partial-time + ## Configuration files What configuration files should we have? Do apps, orch agent each need separate files? diff --git a/fpmsyncd/bgp_eoiu_marker.py b/fpmsyncd/bgp_eoiu_marker.py new file mode 100644 index 00000000000..83051d7878a --- /dev/null +++ b/fpmsyncd/bgp_eoiu_marker.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +"""" +Description: bgp_eoiu_marker.py -- populating bgp eoiu marker flags in stateDB during warm reboot. + The script is started by supervisord in bgp docker when the docker is started. + It does not do anything in case neither system nor bgp warm restart is enabled. + + The script check bgp neighbor state via vtysh cli interface periodically (every 1 second). + It looks for explicit EOR and implicit EOR (keep alive after established) in the json output of show ip bgp neighbors A.B.C.D json + + Once the script has collected all needed EORs, it set a EOIU flags in stateDB. + + fpmsyncd may hold a few seconds (2~5 seconds) after getting the flag before starting routing reconciliation. + 2-5 seconds should be enough for all the route to be synced to fpmsyncd from bgp. If not, the system probably is already in wrong state. + + For any reason the script failed to set EOIU flag in stateDB, the current warm_restart bgp_timer will kick in later. +""" + +import sys +import swsssdk +import time +import syslog +import traceback +import commands +import json +from swsscommon import swsscommon +import errno +from time import gmtime, strftime + +class BgpStateCheck(): + # timeout the restore process in 120 seconds if not finished + # This is in consistent with the default timerout for bgp warm restart set in fpmsyncd + + DEF_TIME_OUT = 120 + + # every 1 seconds to check bgp neighbors state + CHECK_INTERVAL = 1 + def __init__(self): + self.ipv4_neighbors = [] + self.ipv4_neigh_eor_status = {} + self.ipv6_neighbors = [] + self.ipv6_neigh_eor_status = {} + self.keepalivesRecvCnt = {} + self.bgp_ipv4_eoiu = False + self.bgp_ipv6_eoiu = False + self.get_peers_wt = self.DEF_TIME_OUT + + def get_all_peers(self): + while self.get_peers_wt >= 0: + try: + cmd = "vtysh -c 'show bgp summary json'" + output = commands.getoutput(cmd) + peer_info = json.loads(output) + if "ipv4Unicast" in peer_info and "peers" in peer_info["ipv4Unicast"]: + self.ipv4_neighbors = peer_info["ipv4Unicast"]["peers"].keys() + + if "ipv6Unicast" in peer_info and "peers" in peer_info["ipv6Unicast"]: + self.ipv6_neighbors = peer_info["ipv6Unicast"]["peers"].keys() + + syslog.syslog('BGP ipv4 neighbors: {}'.format(self.ipv4_neighbors)) + syslog.syslog('BGP ipv4 neighbors: {}'.format(self.ipv6_neighbors)) + return + + except Exception: + syslog.syslog(syslog.LOG_ERR, "*ERROR* get_all_peers Exception: %s" % (traceback.format_exc())) + time.sleep(5) + self.get_peers_wt -= 5 + self.get_all_peers() + syslog.syslog(syslog.LOG_ERR, "Failed to get bgp neighbor info in {} seconds, exiting".format(self.DEF_TIME_OUT)); + sys.exit(1) + + def init_peers_eor_status(self): + # init neigh eor status to unknown + for neigh in self.ipv4_neighbors: + self.ipv4_neigh_eor_status[neigh] = "unknown" + for neigh in self.ipv6_neighbors: + self.ipv6_neigh_eor_status[neigh] = "unknown" + + # Set the statedb "BGP_STATE_TABLE|eoiu", so fpmsyncd can get the bgp eoiu signal + # Only two families: 'ipv4' and 'ipv6' + # state is "unknown" / "reached" / "consumed" + def set_bgp_eoiu_marker(self, family, state): + db = swsssdk.SonicV2Connector(host='127.0.0.1') + db.connect(db.STATE_DB, False) + key = "BGP_STATE_TABLE|%s|eoiu" % family + db.set(db.STATE_DB, key, 'state', state) + timesamp = strftime("%Y-%m-%d %H:%M:%S", gmtime()) + db.set(db.STATE_DB, key, 'timestamp', timesamp) + db.close(db.STATE_DB) + return + + def clean_bgp_eoiu_marker(self): + db = swsssdk.SonicV2Connector(host='127.0.0.1') + db.connect(db.STATE_DB, False) + db.delete(db.STATE_DB, "BGP_STATE_TABLE|IPv4|eoiu") + db.delete(db.STATE_DB, "BGP_STATE_TABLE|IPv6|eoiu") + db.close(db.STATE_DB) + syslog.syslog('Cleaned ipv4 and ipv6 eoiu marker flags') + return + + def bgp_eor_received(self, neigh, is_ipv4): + try: + neighstr = "%s" % neigh + eor_received = False + cmd = "vtysh -c 'show bgp neighbors %s json'" % neighstr + output = commands.getoutput(cmd) + neig_status = json.loads(output) + if neighstr in neig_status: + if "gracefulRestartInfo" in neig_status[neighstr]: + if "endOfRibRecv" in neig_status[neighstr]["gracefulRestartInfo"]: + eor_info = neig_status[neighstr]["gracefulRestartInfo"]["endOfRibRecv"] + if is_ipv4 and "IPv4 Unicast" in eor_info and eor_info["IPv4 Unicast"] == True: + eor_received = True + elif not is_ipv4 and "IPv6 Unicast" in eor_info and eor_info["IPv6 Unicast"] == True: + eor_received = True + if eor_received: + syslog.syslog('BGP eor received for neighbors: {}'.format(neigh)) + + # No explict eor, try implicit eor + if eor_received == False and "bgpState" in neig_status[neighstr] and neig_status[neighstr]["bgpState"] == "Established": + # if "messageStats" in neig_status and "keepalivesRecv" in neig_status["messageStats"]: + # it looks we need to record the keepalivesRecv count for detecting count change + if neighstr not in self.keepalivesRecvCnt: + self.keepalivesRecvCnt[neighstr] = neig_status[neighstr]["messageStats"]["keepalivesRecv"] + else: + eor_received = (self.keepalivesRecvCnt[neighstr] is not neig_status[neighstr]["messageStats"]["keepalivesRecv"]) + if eor_received: + syslog.syslog('BGP implicit eor received for neighbors: {}'.format(neigh)) + + return eor_received + + except Exception: + syslog.syslog(syslog.LOG_ERR, "*ERROR* bgp_eor_received Exception: %s" % (traceback.format_exc())) + + + # This function is to collect eor state based on the saved ipv4_neigh_eor_status and ipv6_neigh_eor_status dictionaries + # It iterates through the dictionary, and check whether the specific neighbor has EOR received. + # EOR may be explicit EOR (End-Of-RIB) or an implicit-EOR. + # The first keep-alive after BGP has reached Established is considered an implicit-EOR. + # + # ipv4 and ipv6 neighbors are processed separately. + # Once all ipv4 neighbors have EOR received, bgp_ipv4_eoiu becomes True. + # Once all ipv6 neighbors have EOR received, bgp_ipv6_eoiu becomes True. + + # The neighbor EoR states were checked in a loop with an interval (CHECK_INTERVAL) + # The function will timeout in case eoiu states never meet the condition + # after some time (DEF_TIME_OUT). + def wait_for_bgp_eoiu(self): + wait_time = self.DEF_TIME_OUT + while wait_time >= 0: + if not self.bgp_ipv4_eoiu: + for neigh, eor_status in self.ipv4_neigh_eor_status.items(): + if eor_status == "unknown" and self.bgp_eor_received(neigh, True): + self.ipv4_neigh_eor_status[neigh] = "rcvd" + if "unknown" not in self.ipv4_neigh_eor_status.values(): + self.bgp_ipv4_eoiu = True + syslog.syslog("BGP ipv4 eoiu reached") + + if not self.bgp_ipv6_eoiu: + for neigh, eor_status in self.ipv6_neigh_eor_status.items(): + if eor_status == "unknown" and self.bgp_eor_received(neigh, False): + self.ipv6_neigh_eor_status[neigh] = "rcvd" + if "unknown" not in self.ipv6_neigh_eor_status.values(): + self.bgp_ipv6_eoiu = True + syslog.syslog('BGP ipv6 eoiu reached') + + if self.bgp_ipv6_eoiu and self.bgp_ipv4_eoiu: + break; + time.sleep(self.CHECK_INTERVAL) + wait_time -= self.CHECK_INTERVAL + + if not self.bgp_ipv6_eoiu: + syslog.syslog(syslog.LOG_ERR, "BGP ipv6 eoiu not reached: {}".format(self.ipv6_neigh_eor_status)); + + if not self.bgp_ipv4_eoiu: + syslog.syslog(syslog.LOG_ERR, "BGP ipv4 eoiu not reached: {}".format(self.ipv4_neigh_eor_status)); + +def main(): + + print "bgp_eoiu_marker service is started" + + try: + bgp_state_check = BgpStateCheck() + except Exception, e: + syslog.syslog(syslog.LOG_ERR, "{}: error exit 1, reason {}".format(THIS_MODULE, str(e))) + exit(1) + + # Always clean the eoiu marker in stateDB first + bgp_state_check.clean_bgp_eoiu_marker() + + # Use warmstart python binding to check warmstart information + warmstart = swsscommon.WarmStart() + warmstart.initialize("bgp", "bgp") + warmstart.checkWarmStart("bgp", "bgp", False) + + # if bgp or system warm reboot not enabled, don't run + if not warmstart.isWarmStart(): + print "bgp_eoiu_marker service is skipped as warm restart not enabled" + return + + bgp_state_check.set_bgp_eoiu_marker("IPv4", "unknown") + bgp_state_check.set_bgp_eoiu_marker("IPv6", "unknown") + bgp_state_check.get_all_peers() + bgp_state_check.init_peers_eor_status() + try: + bgp_state_check.wait_for_bgp_eoiu() + except Exception as e: + syslog.syslog(syslog.LOG_ERR, str(e)) + sys.exit(1) + + # set statedb to signal other processes like fpmsynd + if bgp_state_check.bgp_ipv4_eoiu: + bgp_state_check.set_bgp_eoiu_marker("IPv4", "reached") + if bgp_state_check.bgp_ipv6_eoiu: + bgp_state_check.set_bgp_eoiu_marker("IPv6", "reached") + + print "bgp_eoiu_marker service is done" + return + +if __name__ == '__main__': + main() diff --git a/fpmsyncd/fpm/fpm.h b/fpmsyncd/fpm/fpm.h index 96f05f48657..8af9b30ae9e 100644 --- a/fpmsyncd/fpm/fpm.h +++ b/fpmsyncd/fpm/fpm.h @@ -221,7 +221,7 @@ fpm_msg_next (fpm_msg_hdr_t *hdr, size_t *len) *len -= msg_len; } - return (fpm_msg_hdr_t *) (((char*) hdr) + msg_len); + return reinterpret_cast(static_cast(((char*) hdr) + msg_len)); } /* diff --git a/fpmsyncd/fpmlink.cpp b/fpmsyncd/fpmlink.cpp index 2147f85d7df..464f9652217 100644 --- a/fpmsyncd/fpmlink.cpp +++ b/fpmsyncd/fpmlink.cpp @@ -89,7 +89,7 @@ int FpmLink::getFd() return m_connection_socket; } -void FpmLink::readData() +uint64_t FpmLink::readData() { fpm_msg_hdr_t *hdr; size_t msg_len; @@ -106,7 +106,7 @@ void FpmLink::readData() /* Check for complete messages */ while (true) { - hdr = (fpm_msg_hdr_t *)(m_messageBuffer + start); + hdr = reinterpret_cast(static_cast(m_messageBuffer + start)); left = m_pos - start; if (left < FPM_MSG_HDR_LEN) break; @@ -133,4 +133,5 @@ void FpmLink::readData() memmove(m_messageBuffer, m_messageBuffer + start, m_pos - start); m_pos = m_pos - (uint32_t)start; + return 0; } diff --git a/fpmsyncd/fpmlink.h b/fpmsyncd/fpmlink.h index 7cd0635ed12..8ef570589f8 100644 --- a/fpmsyncd/fpmlink.h +++ b/fpmsyncd/fpmlink.h @@ -26,7 +26,7 @@ class FpmLink : public Selectable { void accept(); int getFd() override; - void readData() override; + uint64_t readData() override; /* readMe throws FpmConnectionClosedException when connection is lost */ class FpmConnectionClosedException : public std::exception { diff --git a/fpmsyncd/fpmsyncd.cpp b/fpmsyncd/fpmsyncd.cpp index 42eba65f0f5..8040a7ecb47 100644 --- a/fpmsyncd/fpmsyncd.cpp +++ b/fpmsyncd/fpmsyncd.cpp @@ -1,4 +1,5 @@ #include +#include #include "logger.h" #include "select.h" #include "selectabletimer.h" @@ -18,6 +19,30 @@ using namespace swss; */ const uint32_t DEFAULT_ROUTING_RESTART_INTERVAL = 120; +// Wait 3 seconds after detecting EOIU reached state +// TODO: support eoiu hold interval config +const uint32_t DEFAULT_EOIU_HOLD_INTERVAL = 3; + +// Check if eoiu state reached by both ipv4 and ipv6 +static bool eoiuFlagsSet(Table &bgpStateTable) +{ + string value; + + bgpStateTable.hget("IPv4|eoiu", "state", value); + if (value != "reached") + { + SWSS_LOG_DEBUG("IPv4|eoiu state: %s", value.c_str()); + return false; + } + bgpStateTable.hget("IPv6|eoiu", "state", value); + if (value != "reached") + { + SWSS_LOG_DEBUG("IPv6|eoiu state: %s", value.c_str()); + return false; + } + SWSS_LOG_NOTICE("Warm-Restart bgp eoiu reached for both ipv4 and ipv6"); + return true; +} int main(int argc, char **argv) { @@ -26,6 +51,9 @@ int main(int argc, char **argv) RedisPipeline pipeline(&db); RouteSync sync(&pipeline); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + Table bgpStateTable(&stateDb, STATE_BGP_TABLE_NAME); + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, &sync); NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, &sync); @@ -36,7 +64,10 @@ int main(int argc, char **argv) FpmLink fpm; Select s; SelectableTimer warmStartTimer(timespec{0, 0}); - + // Before eoiu flags detected, check them periodically. It also stop upon detection of reconciliation done. + SelectableTimer eoiuCheckTimer(timespec{0, 0}); + // After eoiu flags are detected, start a hold timer before starting reconciliation. + SelectableTimer eoiuHoldTimer(timespec{0, 0}); /* * Pipeline should be flushed right away to deal with state pending * from previous try/catch iterations. @@ -54,7 +85,7 @@ int main(int argc, char **argv) if (warmStartEnabled) { /* Obtain warm-restart timer defined for routing application */ - uint32_t warmRestartIval = sync.m_warmStartHelper.getRestartTimer(); + time_t warmRestartIval = sync.m_warmStartHelper.getRestartTimer(); if (!warmRestartIval) { warmStartTimer.setInterval(timespec{DEFAULT_ROUTING_RESTART_INTERVAL, 0}); @@ -69,7 +100,14 @@ int main(int argc, char **argv) { warmStartTimer.start(); s.addSelectable(&warmStartTimer); + SWSS_LOG_NOTICE("Warm-Restart timer started."); } + + // Also start periodic eoiu check timer, first wait 5 seconds, then check every 1 second + eoiuCheckTimer.setInterval(timespec{5, 0}); + eoiuCheckTimer.start(); + s.addSelectable(&eoiuCheckTimer); + SWSS_LOG_NOTICE("Warm-Restart eoiuCheckTimer timer started."); } while (true) @@ -80,19 +118,64 @@ int main(int argc, char **argv) s.select(&temps); /* - * Upon expiration of the warm-restart timer, proceed to run the - * reconciliation process and remove warm-restart timer from + * Upon expiration of the warm-restart timer or eoiu Hold Timer, proceed to run the + * reconciliation process if not done yet and remove the timer from * select() loop. + * Note: route reconciliation always succeeds, it will not be done twice. */ - if (warmStartEnabled && temps == &warmStartTimer) + if (temps == &warmStartTimer || temps == &eoiuHoldTimer) { - SWSS_LOG_NOTICE("Warm-Restart timer expired."); - sync.m_warmStartHelper.reconcile(); - s.removeSelectable(&warmStartTimer); - + if (temps == &warmStartTimer) + { + SWSS_LOG_NOTICE("Warm-Restart timer expired."); + } + else + { + SWSS_LOG_NOTICE("Warm-Restart EOIU hold timer expired."); + } + if (sync.m_warmStartHelper.inProgress()) + { + sync.m_warmStartHelper.reconcile(); + SWSS_LOG_NOTICE("Warm-Restart reconciliation processed."); + } + // remove the one-shot timer. + s.removeSelectable(temps); pipeline.flush(); SWSS_LOG_DEBUG("Pipeline flushed"); } + else if (temps == &eoiuCheckTimer) + { + if (sync.m_warmStartHelper.inProgress()) + { + if (eoiuFlagsSet(bgpStateTable)) + { + /* Obtain eoiu hold timer defined for bgp docker */ + uintmax_t eoiuHoldIval = WarmStart::getWarmStartTimer("eoiu_hold", "bgp"); + if (!eoiuHoldIval) + { + eoiuHoldTimer.setInterval(timespec{DEFAULT_EOIU_HOLD_INTERVAL, 0}); + eoiuHoldIval = DEFAULT_EOIU_HOLD_INTERVAL; + } + else + { + eoiuHoldTimer.setInterval(timespec{(time_t)eoiuHoldIval, 0}); + } + eoiuHoldTimer.start(); + s.addSelectable(&eoiuHoldTimer); + SWSS_LOG_NOTICE("Warm-Restart started EOIU hold timer which is to expire in %" PRIuMAX " seconds.", eoiuHoldIval); + s.removeSelectable(&eoiuCheckTimer); + continue; + } + eoiuCheckTimer.setInterval(timespec{1, 0}); + // re-start eoiu check timer + eoiuCheckTimer.start(); + SWSS_LOG_DEBUG("Warm-Restart eoiuCheckTimer restarted"); + } + else + { + s.removeSelectable(&eoiuCheckTimer); + } + } else if (!warmStartEnabled || sync.m_warmStartHelper.isReconciled()) { pipeline.flush(); diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index 8f05338f2d5..6f453729651 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -45,16 +45,25 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj) unsigned int master_index = rtnl_route_get_table(route_obj); char master_name[IFNAMSIZ] = {0}; - /* Get the name of the master device */ - getIfName(master_index, master_name, IFNAMSIZ); - - /* If the master device name starts with VNET_PREFIX, it is a VNET route. - The VNET name is exactly the name of the associated master device. */ - if (string(master_name).find(VNET_PREFIX) == 0) + /* if the table_id is not set in the route obj then route is for default vrf. */ + if (master_index) { - onVnetRouteMsg(nlmsg_type, obj, string(master_name)); + /* Get the name of the master device */ + getIfName(master_index, master_name, IFNAMSIZ); + + /* If the master device name starts with VNET_PREFIX, it is a VNET route. + The VNET name is exactly the name of the associated master device. */ + if (string(master_name).find(VNET_PREFIX) == 0) + { + onVnetRouteMsg(nlmsg_type, obj, string(master_name)); + } + /* Otherwise, it is a regular route (include VRF route). */ + else + { + onRouteMsg(nlmsg_type, obj); + } + } - /* Otherwise, it is a regular route (include VRF route). */ else { onRouteMsg(nlmsg_type, obj); diff --git a/neighsyncd/restore_neighbors.py b/neighsyncd/restore_neighbors.py index d0851c632ae..73453ce7d2a 100755 --- a/neighsyncd/restore_neighbors.py +++ b/neighsyncd/restore_neighbors.py @@ -25,11 +25,29 @@ from scapy.all import conf, in6_getnsma, inet_pton, inet_ntop, in6_getnsmac, get_if_hwaddr, Ether, ARP, IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr from swsscommon import swsscommon import errno +import syslog logger = logging.getLogger(__name__) logger.setLevel(logging.WARNING) logger.addHandler(logging.NullHandler()) +SYSLOG_IDENTIFIER = 'restore_neighbor' + +def log_info(msg): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_INFO, msg) + syslog.closelog() + +def log_warning(msg): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + +def log_error(msg): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + # timeout the restore process in 110 seconds if not finished # This is mostly to wait for interfaces to be created and up after system warm-reboot # and this process is started by supervisord in swss docker. @@ -58,12 +76,27 @@ def is_intf_oper_state_up(intf): state_file = open(oper_file.format(intf), 'r') state = state_file.readline().rstrip() except Exception as e: - logger.info('Error: {}'.format(str(e))) + log_info('Error: {}'.format(str(e))) return False if state == '1': return True return False +def is_intf_up(intf, db): + if not is_intf_oper_state_up(intf): + return False + if 'Vlan' in intf: + table_name = 'VLAN_MEMBER_TABLE|{}|*'.format(intf) + key = db.keys(db.STATE_DB, table_name) + if key is None: + log_info ("Vlan member is not yet created") + return False + if is_intf_up.counter == 0: + time.sleep(3*CHECK_INTERVAL) + is_intf_up.counter = 1 + log_info ("intf {} is up".format(intf)) + return True + # read the neigh table from AppDB to memory, format as below # build map as below, this can efficiently access intf and family groups later # { intf1 -> { { family1 -> [[ip1, mac1], [ip2, mac2] ...] } @@ -131,7 +164,7 @@ def read_neigh_table_to_maps(): # Use netlink to set neigh table into kernel, not overwrite the existing ones def set_neigh_in_kernel(ipclass, family, intf_idx, dst_ip, dmac): - logging.info('Add neighbor entries: family: {}, intf_idx: {}, ip: {}, mac: {}'.format( + log_info('Add neighbor entries: family: {}, intf_idx: {}, ip: {}, mac: {}'.format( family, intf_idx, dst_ip, dmac)) if family not in ip_family: @@ -152,7 +185,7 @@ def set_neigh_in_kernel(ipclass, family, intf_idx, dst_ip, dmac): # If neigh exists, log it but no exception raise, other exceptions, raise except NetlinkError as e: if e[0] == errno.EEXIST: - logger.warning('Neigh exists in kernel with family: {}, intf_idx: {}, ip: {}, mac: {}'.format( + log_warning('Neigh exists in kernel with family: {}, intf_idx: {}, ip: {}, mac: {}'.format( family, intf_idx, dst_ip, dmac)) else: raise @@ -196,10 +229,13 @@ def restore_update_kernel_neighbors(intf_neigh_map, timeout=DEF_TIME_OUT): ipclass = IPRoute() mtime = monotonic.time.time start_time = mtime() + is_intf_up.counter = 0 + db = swsssdk.SonicV2Connector(host='127.0.0.1') + db.connect(db.STATE_DB, False) while (mtime() - start_time) < timeout: for intf, family_neigh_map in intf_neigh_map.items(): # only try to restore to kernel when link is up - if is_intf_oper_state_up(intf): + if is_intf_up(intf, db): src_mac = get_if_hwaddr(intf) intf_idx = ipclass.link_lookup(ifname=intf)[0] # create socket per intf to send packets @@ -215,6 +251,8 @@ def restore_update_kernel_neighbors(intf_neigh_map, timeout=DEF_TIME_OUT): # use netlink to set neighbor entries set_neigh_in_kernel(ipclass, family, intf_idx, dst_ip, dmac) + log_info('Sending Neigh with family: {}, intf_idx: {}, ip: {}, mac: {}'.format( + family, intf_idx, dst_ip, dmac)) # sending arp/ns packet to update kernel neigh info s.send(build_arp_ns_pkt(family, src_mac, src_ip, dst_ip)) # delete this family on the intf @@ -229,12 +267,12 @@ def restore_update_kernel_neighbors(intf_neigh_map, timeout=DEF_TIME_OUT): if not intf_neigh_map: break time.sleep(CHECK_INTERVAL) + db.close(db.STATE_DB) def main(): - print "restore_neighbors service is started" - + log_info ("restore_neighbors service is started") # Use warmstart python binding to check warmstart information warmstart = swsscommon.WarmStart() warmstart.initialize("neighsyncd", "swss") @@ -242,15 +280,14 @@ def main(): # if swss or system warm reboot not enabled, don't run if not warmstart.isWarmStart(): - print "restore_neighbors service is skipped as warm restart not enabled" + log_info ("restore_neighbors service is skipped as warm restart not enabled") return # swss restart not system warm reboot, set statedb directly if not warmstart.isSystemWarmRebootEnabled(): set_statedb_neigh_restore_done() - print "restore_neighbors service is done as system warm reboot not enabled" + log_info ("restore_neighbors service is done as system warm reboot not enabled") return - # read the neigh table from appDB to internal map try: intf_neigh_map = read_neigh_table_to_maps() @@ -266,7 +303,7 @@ def main(): # set statedb to signal other processes like neighsyncd set_statedb_neigh_restore_done() - print "restore_neighbor service is done for system warmreboot" + log_info ("restore_neighbor service is done for system warmreboot") return if __name__ == '__main__': diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 0fdbba21f2f..3327079a280 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -5,6 +5,7 @@ CFLAGS_SAI = -I /usr/include/sai swssdir = $(datadir)/swss dist_swss_DATA = \ + pfc_detect_innovium.lua \ pfc_detect_mellanox.lua \ pfc_detect_broadcom.lua \ pfc_detect_barefoot.lua \ @@ -55,7 +56,7 @@ orchagent_SOURCES = \ orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -orchagent_LDADD = -lnl-3 -lnl-route-3 -lpthread -lsairedis -lswsscommon -lsaimetadata +orchagent_LDADD = -lnl-3 -lnl-route-3 -lpthread -lsairedis -lswsscommon -lsaimeta -lsaimetadata routeresync_SOURCES = routeresync.cpp routeresync_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index fb79a2f1578..21496c05fdd 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,6 +10,7 @@ #include "tokenize.h" #include "timer.h" #include "crmorch.h" +#include "sai_serialize.h" using namespace std; using namespace swss; @@ -59,14 +61,19 @@ acl_rule_attr_lookup_t aclMatchLookup = { MATCH_INNER_L4_DST_PORT, SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_DST_PORT } }; -acl_rule_attr_lookup_t aclL3ActionLookup = +static acl_rule_attr_lookup_t aclL3ActionLookup = { - { PACKET_ACTION_FORWARD, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, - { PACKET_ACTION_DROP, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, - { PACKET_ACTION_REDIRECT, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT } + { ACTION_PACKET_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, + { ACTION_REDIRECT_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT }, }; -acl_rule_attr_lookup_t aclDTelActionLookup = +static acl_rule_attr_lookup_t aclMirrorStageLookup = +{ + { ACTION_MIRROR_INGRESS_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS}, + { ACTION_MIRROR_EGRESS_ACTION, SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_EGRESS}, +}; + +static acl_rule_attr_lookup_t aclDTelActionLookup = { { ACTION_DTEL_FLOW_OP, SAI_ACL_ENTRY_ATTR_ACTION_ACL_DTEL_FLOW_OP }, { ACTION_DTEL_INT_SESSION, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_INT_SESSION }, @@ -76,7 +83,13 @@ acl_rule_attr_lookup_t aclDTelActionLookup = { ACTION_DTEL_REPORT_ALL_PACKETS, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_REPORT_ALL_PACKETS } }; -acl_dtel_flow_op_type_lookup_t aclDTelFlowOpTypeLookup = +static acl_packet_action_lookup_t aclPacketActionLookup = +{ + { PACKET_ACTION_FORWARD, SAI_PACKET_ACTION_FORWARD }, + { PACKET_ACTION_DROP, SAI_PACKET_ACTION_DROP }, +}; + +static acl_dtel_flow_op_type_lookup_t aclDTelFlowOpTypeLookup = { { DTEL_FLOW_OP_NOP, SAI_ACL_DTEL_FLOW_OP_NOP }, { DTEL_FLOW_OP_POSTCARD, SAI_ACL_DTEL_FLOW_OP_POSTCARD }, @@ -98,8 +111,8 @@ static acl_table_type_lookup_t aclTableTypeLookUp = static acl_stage_type_lookup_t aclStageLookUp = { - {TABLE_INGRESS, ACL_STAGE_INGRESS }, - {TABLE_EGRESS, ACL_STAGE_EGRESS } + {STAGE_INGRESS, ACL_STAGE_INGRESS }, + {STAGE_EGRESS, ACL_STAGE_EGRESS } }; static acl_ip_type_lookup_t aclIpTypeLookup = @@ -378,6 +391,39 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) return true; } +bool AclRule::validateAddAction(string attr_name, string attr_value) +{ + for (const auto& it: m_actions) + { + if (!AclRule::isActionSupported(it.first)) + { + SWSS_LOG_ERROR("Action %s:%s is not supported by ASIC", + attr_name.c_str(), attr_value.c_str()); + return false; + } + + // check if ACL action attribute entry parameter is an enum value + const auto* meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_ACL_ENTRY, it.first); + if (meta == nullptr) + { + SWSS_LOG_THROW("Metadata null pointer returned by sai_metadata_get_attr_metadata for action %s", + attr_name.c_str()); + } + if (meta->isenum) + { + // if ACL action attribute requires enum value check if value is supported by the ASIC + if (!m_pAclOrch->isAclActionEnumValueSupported(AclOrch::getAclActionFromAclEntry(it.first), + it.second.aclaction.parameter)) + { + SWSS_LOG_ERROR("Action %s:%s is not supported by ASIC", + attr_name.c_str(), attr_value.c_str()); + return false; + } + } + } + return true; +} + bool AclRule::processIpType(string type, sai_uint32_t &ip_type) { SWSS_LOG_ENTER(); @@ -526,6 +572,17 @@ void AclRule::decreaseNextHopRefCount() return; } +bool AclRule::isActionSupported(sai_acl_entry_attr_t action) const +{ + auto action_type = AclOrch::getAclActionFromAclEntry(action); + const auto* pTable = m_pAclOrch->getTableByOid(m_tableOid); + if (pTable == nullptr) + { + SWSS_LOG_THROW("ACL table does not exist for oid %lu", m_tableOid); + } + return m_pAclOrch->isAclActionSupported(pTable->stage, action_type); +} + bool AclRule::remove() { SWSS_LOG_ENTER(); @@ -583,12 +640,11 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { string attr_name = to_upper(fvField(itr)); string attr_value = fvValue(itr); - if (attr_name == ACTION_PACKET_ACTION || attr_name == ACTION_MIRROR_ACTION || - attr_name == ACTION_DTEL_FLOW_OP || attr_name == ACTION_DTEL_INT_SESSION || - attr_name == ACTION_DTEL_DROP_REPORT_ENABLE || - attr_name == ACTION_DTEL_TAIL_DROP_REPORT_ENABLE || - attr_name == ACTION_DTEL_FLOW_SAMPLE_PERCENT || - attr_name == ACTION_DTEL_REPORT_ALL_PACKETS) + if (aclL3ActionLookup.find(attr_name) != aclL3ActionLookup.cend() || + aclMirrorStageLookup.find(attr_name) != aclMirrorStageLookup.cend() || + /* handle "MIRROR_ACTION" key without mirror stage specified for backward compatibility */ + attr_name == ACTION_MIRROR_ACTION || + aclDTelActionLookup.find(attr_name) != aclDTelActionLookup.cend()) { action_found = true; action = attr_name; @@ -613,7 +669,8 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir } /* Mirror rules can exist in both tables */ - if (action == ACTION_MIRROR_ACTION) + if (aclMirrorStageLookup.find(action) != aclMirrorStageLookup.cend() || + action == ACTION_MIRROR_ACTION /* implicitly ingress in old schema */) { return make_shared(acl, mirror, rule, table, type); } @@ -715,7 +772,7 @@ bool AclRule::removeCounter() gCrmOrch->decCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_COUNTER, m_tableOid); - SWSS_LOG_INFO("Removing record about the counter %lX from the DB", m_counterOid); + SWSS_LOG_INFO("Removing record about the counter %" PRIx64 " from the DB", m_counterOid); AclOrch::getCountersTable().del(getTableId() + ":" + getId()); m_counterOid = SAI_NULL_OBJECT_ID; @@ -735,24 +792,37 @@ bool AclRuleL3::validateAddAction(string attr_name, string _attr_value) string attr_value = to_upper(_attr_value); sai_attribute_value_t value; - if (attr_name != ACTION_PACKET_ACTION) - { - return false; - } + auto action_str = attr_name; - if (attr_value == PACKET_ACTION_FORWARD) + if (attr_name == ACTION_PACKET_ACTION) { - value.aclaction.parameter.s32 = SAI_PACKET_ACTION_FORWARD; - } - else if (attr_value == PACKET_ACTION_DROP) - { - value.aclaction.parameter.s32 = SAI_PACKET_ACTION_DROP; + const auto it = aclPacketActionLookup.find(attr_value); + if (it != aclPacketActionLookup.cend()) + { + value.aclaction.parameter.s32 = it->second; + } + // handle PACKET_ACTION_REDIRECT in ACTION_PACKET_ACTION for backward compatibility + else if (attr_value.find(PACKET_ACTION_REDIRECT) != string::npos) + { + // resize attr_value to remove argument, _attr_value still has the argument + attr_value.resize(string(PACKET_ACTION_REDIRECT).length()); + + sai_object_id_t param_id = getRedirectObjectId(_attr_value); + if (param_id == SAI_NULL_OBJECT_ID) + { + return false; + } + value.aclaction.parameter.oid = param_id; + + action_str = ACTION_REDIRECT_ACTION; + } + else + { + return false; + } } - else if (attr_value.find(PACKET_ACTION_REDIRECT) != string::npos) + else if (attr_name == ACTION_REDIRECT_ACTION) { - // resize attr_value to remove argument, _attr_value still has the argument - attr_value.resize(string(PACKET_ACTION_REDIRECT).length()); - sai_object_id_t param_id = getRedirectObjectId(_attr_value); if (param_id == SAI_NULL_OBJECT_ID) { @@ -764,11 +834,12 @@ bool AclRuleL3::validateAddAction(string attr_name, string _attr_value) { return false; } + value.aclaction.enable = true; - m_actions[aclL3ActionLookup[attr_value]] = value; + m_actions[aclL3ActionLookup[action_str]] = value; - return true; + return AclRule::validateAddAction(attr_name, attr_value); } // This method should return sai attribute id of the redirect destination @@ -956,19 +1027,35 @@ bool AclRuleMirror::validateAddAction(string attr_name, string attr_value) { SWSS_LOG_ENTER(); - if (attr_name != ACTION_MIRROR_ACTION) + sai_acl_entry_attr_t action; + + const auto it = aclMirrorStageLookup.find(attr_name); + if (it != aclMirrorStageLookup.cend()) + { + action = it->second; + } + // handle ACTION_MIRROR_ACTION as ingress by default for backward compatibility + else if (attr_name == ACTION_MIRROR_ACTION) + { + action = SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS; + } + else { return false; } - if (!m_pMirrorOrch->sessionExists(attr_value)) + m_sessionName = attr_value; + + if (!m_pMirrorOrch->sessionExists(m_sessionName)) { + SWSS_LOG_ERROR("Mirror rule reference mirror session that does not exists %s", m_sessionName.c_str()); return false; } - m_sessionName = attr_value; + // insert placeholder value, we'll set the session oid in AclRuleMirror::create() + m_actions[action] = sai_attribute_value_t{}; - return true; + return AclRule::validateAddAction(attr_name, attr_value); } bool AclRuleMirror::validateAddMatch(string attr_name, string attr_value) @@ -1048,20 +1135,19 @@ bool AclRuleMirror::create() { SWSS_LOG_ENTER(); - sai_attribute_value_t value; - bool state = false; sai_object_id_t oid = SAI_NULL_OBJECT_ID; + bool state = false; if (!m_pMirrorOrch->getSessionStatus(m_sessionName, state)) { - throw runtime_error("Failed to get mirror session state"); + SWSS_LOG_THROW("Failed to get mirror session state for session %s", m_sessionName.c_str()); } // Increase session reference count regardless of state to deny // attempt to remove mirror session with attached ACL rules. if (!m_pMirrorOrch->increaseRefCount(m_sessionName)) { - throw runtime_error("Failed to increase mirror session reference count"); + SWSS_LOG_THROW("Failed to increase mirror session reference count for session %s", m_sessionName.c_str()); } if (!state) @@ -1071,15 +1157,15 @@ bool AclRuleMirror::create() if (!m_pMirrorOrch->getSessionOid(m_sessionName, oid)) { - throw runtime_error("Failed to get mirror session OID"); + SWSS_LOG_THROW("Failed to get mirror session OID for session %s", m_sessionName.c_str()); } - value.aclaction.enable = true; - value.aclaction.parameter.objlist.list = &oid; - value.aclaction.parameter.objlist.count = 1; - - m_actions.clear(); - m_actions[SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS] = value; + for (auto& it: m_actions) + { + it.second.aclaction.enable = true; + it.second.aclaction.parameter.objlist.list = &oid; + it.second.aclaction.parameter.objlist.count = 1; + } if (!AclRule::create()) { @@ -1445,7 +1531,7 @@ bool AclTable::unbind(sai_object_id_t portOid) sai_object_id_t member = ports[portOid]; sai_status_t status = sai_acl_api->remove_acl_table_group_member(member); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to unbind table %lu as member %lu from ACL table: %d", + SWSS_LOG_ERROR("Failed to unbind table %" PRIu64 " as member %" PRIu64 " from ACL table: %d", m_oid, member, status); return false; } @@ -1497,19 +1583,22 @@ bool AclTable::add(shared_ptr newRule) if (ruleIter->second->remove()) { rules.erase(ruleIter); - SWSS_LOG_NOTICE("Successfully deleted ACL rule: %s", rule_id.c_str()); + SWSS_LOG_NOTICE("Successfully deleted ACL rule %s in table %s", + rule_id.c_str(), id.c_str()); } } if (newRule->create()) { rules[rule_id] = newRule; - SWSS_LOG_NOTICE("Successfully created ACL rule %s in table %s", rule_id.c_str(), id.c_str()); + SWSS_LOG_NOTICE("Successfully created ACL rule %s in table %s", + rule_id.c_str(), id.c_str()); return true; } else { - SWSS_LOG_ERROR("Failed to create rule in table %s", id.c_str()); + SWSS_LOG_ERROR("Failed to create ACL rule %s in table %s", + rule_id.c_str(), id.c_str()); return false; } } @@ -1524,18 +1613,21 @@ bool AclTable::remove(string rule_id) if (ruleIter->second->remove()) { rules.erase(ruleIter); - SWSS_LOG_NOTICE("Successfully deleted ACL rule %s", rule_id.c_str()); + SWSS_LOG_NOTICE("Successfully deleted ACL rule %s in table %s", + rule_id.c_str(), id.c_str()); return true; } else { - SWSS_LOG_ERROR("Failed to delete ACL rule: %s", rule_id.c_str()); + SWSS_LOG_ERROR("Failed to delete ACL rule %s in table %s", + rule_id.c_str(), id.c_str()); return false; } } else { - SWSS_LOG_WARN("Skip deleting ACL rule. Unknown rule %s", rule_id.c_str()); + SWSS_LOG_WARN("Skip deleting unknown ACL rule %s in table %s", + rule_id.c_str(), id.c_str()); return true; } } @@ -1550,7 +1642,8 @@ bool AclTable::clear() bool suc = rule.remove(); if (!suc) { - SWSS_LOG_ERROR("Failed to delete existing ACL rule %s when removing the ACL table %s", rule.getId().c_str(), id.c_str()); + SWSS_LOG_ERROR("Failed to delete ACL rule %s when removing the ACL table %s", + rule.getId().c_str(), id.c_str()); return false; } } @@ -1657,7 +1750,7 @@ bool AclRuleDTelFlowWatchListEntry::validateAddAction(string attr_name, string a m_actions[aclDTelActionLookup[attr_name]] = value; - return true; + return AclRule::validateAddAction(attr_name, attr_value); } bool AclRuleDTelFlowWatchListEntry::validate() @@ -1816,7 +1909,7 @@ bool AclRuleDTelDropWatchListEntry::validateAddAction(string attr_name, string a m_actions[aclDTelActionLookup[attr_name]] = value; - return true; + return AclRule::validateAddAction(attr_name, attr_value); } bool AclRuleDTelDropWatchListEntry::validate() @@ -1888,14 +1981,14 @@ AclRange *AclRange::create(sai_acl_range_type_t type, int min, int max) return NULL; } - SWSS_LOG_INFO("Created ACL Range object. Type: %d, range %d-%d, oid: %lX", type, min, max, range_oid); + SWSS_LOG_INFO("Created ACL Range object. Type: %d, range %d-%d, oid: %" PRIx64, type, min, max, range_oid); m_ranges[rangeProperties] = new AclRange(type, range_oid, min, max); range_it = m_ranges.find(rangeProperties); } else { - SWSS_LOG_INFO("Reusing range object oid %lX ref count increased to %d", range_it->second->m_oid, range_it->second->m_refCnt); + SWSS_LOG_INFO("Reusing range object oid %" PRIx64 " ref count increased to %d", range_it->second->m_oid, range_it->second->m_refCnt); } // increase range reference count @@ -1947,10 +2040,10 @@ bool AclRange::remove() if (m_refCnt == 0) { - SWSS_LOG_INFO("Range object oid %lX ref count is %d, removing..", m_oid, m_refCnt); + SWSS_LOG_INFO("Range object oid %" PRIx64 " ref count is %d, removing..", m_oid, m_refCnt); if (sai_acl_api->remove_acl_range(m_oid) != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to delete ACL Range object oid: %lX", m_oid); + SWSS_LOG_ERROR("Failed to delete ACL Range object oid: %" PRIx64, m_oid); return false; } auto range_it = m_ranges.find(make_tuple(m_type, m_min, m_max)); @@ -1960,7 +2053,7 @@ bool AclRange::remove() } else { - SWSS_LOG_INFO("Range object oid %lX ref count decreased to %d", m_oid, m_refCnt); + SWSS_LOG_INFO("Range object oid %" PRIx64 " ref count decreased to %d", m_oid, m_refCnt); } return true; @@ -1976,7 +2069,8 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr // purposes. string platform = getenv("platform") ? getenv("platform") : ""; if (platform == BRCM_PLATFORM_SUBSTRING || - platform == MLNX_PLATFORM_SUBSTRING) + platform == MLNX_PLATFORM_SUBSTRING || + platform == NPS_PLATFORM_SUBSTRING) { m_mirrorTableCapabilities = { @@ -2000,7 +2094,8 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr m_mirrorTableCapabilities[ACL_TABLE_MIRRORV6] ? "yes" : "no"); // In Broadcom platform, V4 and V6 rules are stored in the same table - if (platform == BRCM_PLATFORM_SUBSTRING) { + if (platform == BRCM_PLATFORM_SUBSTRING || + platform == NPS_PLATFORM_SUBSTRING) { m_isCombinedMirrorV6Table = true; } @@ -2045,6 +2140,8 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr throw "AclOrch initialization failure"; } + queryAclActionCapability(); + // Attach observers m_mirrorOrch->attach(this); gPortsOrch->attach(this); @@ -2058,6 +2155,181 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr timer->start(); } +void AclOrch::queryAclActionCapability() +{ + SWSS_LOG_ENTER(); + + sai_status_t status {SAI_STATUS_FAILURE}; + sai_attribute_t attr; + vector action_list; + vector fvVector; + + attr.id = SAI_SWITCH_ATTR_MAX_ACL_ACTION_COUNT; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_THROW("AclOrch initialization failed: " + "failed to query maximum ACL action count"); + } + + const auto max_action_count = attr.value.u32; + + for (auto stage_attr: {SAI_SWITCH_ATTR_ACL_STAGE_INGRESS, SAI_SWITCH_ATTR_ACL_STAGE_EGRESS}) + { + auto stage = (stage_attr == SAI_SWITCH_ATTR_ACL_STAGE_INGRESS ? ACL_STAGE_INGRESS : ACL_STAGE_EGRESS); + auto stage_str = (stage_attr == SAI_SWITCH_ATTR_ACL_STAGE_INGRESS ? STAGE_INGRESS : STAGE_EGRESS); + action_list.resize(static_cast(max_action_count)); + + attr.id = stage_attr; + attr.value.aclcapability.action_list.list = action_list.data(); + attr.value.aclcapability.action_list.count = max_action_count; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_THROW("AclOrch initialization failed: " + "failed to query supported %s ACL actions", stage_str); + } + + SWSS_LOG_INFO("Supported %s action count %d:", stage_str, + attr.value.aclcapability.action_list.count); + + for (size_t i = 0; i < static_cast(attr.value.aclcapability.action_list.count); i++) + { + auto action = static_cast(action_list[i]); + m_aclCapabilities[stage].insert(action); + SWSS_LOG_INFO(" %s", sai_serialize_enum(action, &sai_metadata_enum_sai_acl_action_type_t).c_str()); + } + + // put capabilities in state DB + + auto field = std::string("ACL_ACTIONS") + '|' + stage_str; + auto& acl_action_set = m_aclCapabilities[stage]; + + string delimiter; + ostringstream acl_action_value_stream; + + for (const auto& action_map: {aclL3ActionLookup, aclMirrorStageLookup, aclDTelActionLookup}) + { + for (const auto& it: action_map) + { + auto saiAction = getAclActionFromAclEntry(it.second); + if (acl_action_set.find(saiAction) != acl_action_set.cend()) + { + acl_action_value_stream << delimiter << it.first; + delimiter = comma; + } + } + } + + fvVector.emplace_back(field, acl_action_value_stream.str()); + m_switchTable.set("switch", fvVector); + } + + /* For those ACL action entry attributes for which acl parameter is enumeration (metadata->isenum == true) + * we can query enum values which are implemented by vendor SAI. + * For this purpose we may want to use "sai_query_attribute_enum_values_capability" + * from SAI object API call (saiobject.h). + * However, right now libsairedis does not support SAI object API, so we will just + * put all values as supported for now. + */ + + queryAclActionAttrEnumValues(ACTION_PACKET_ACTION, + aclL3ActionLookup, + aclPacketActionLookup); + queryAclActionAttrEnumValues(ACTION_DTEL_FLOW_OP, + aclDTelActionLookup, + aclDTelFlowOpTypeLookup); +} + +template +void AclOrch::queryAclActionAttrEnumValues(const string &action_name, + const acl_rule_attr_lookup_t& ruleAttrLookupMap, + const AclActionAttrLookupT lookupMap) +{ + vector fvVector; + auto acl_attr = ruleAttrLookupMap.at(action_name); + auto acl_action = getAclActionFromAclEntry(acl_attr); + + /* if the action is not supported then no need to do secondary query for + * supported values + */ + if (isAclActionSupported(ACL_STAGE_INGRESS, acl_action) || + isAclActionSupported(ACL_STAGE_EGRESS, acl_action)) + { + string delimiter; + ostringstream acl_action_value_stream; + auto field = std::string("ACL_ACTION") + '|' + action_name; + + const auto* meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_ACL_ENTRY, acl_attr); + if (meta == nullptr) + { + SWSS_LOG_THROW("Metadata null pointer returned by sai_metadata_get_attr_metadata for action %s", + action_name.c_str()); + } + + if (!meta->isenum) + { + SWSS_LOG_THROW("%s is not an enum", action_name.c_str()); + } + + // TODO: once sai object api is available make this code compile +#ifdef SAIREDIS_SUPPORT_OBJECT_API + vector values_list(meta->enummetadata->valuescount); + sai_s32_list_t values; + values.count = static_cast(values_list.size()); + values.list = values_list.data(); + + auto status = sai_query_attribute_enum_values_capability(gSwitchId, + SAI_OBJECT_TYPE_ACL_ENTRY, + acl_attr, + &values); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_THROW("sai_query_attribute_enum_values_capability failed for %s", + action_name.c_str()); + } + + for (size_t i = 0; i < values.count; i++) + { + m_aclEnumActionCapabilities[acl_action].insert(values.list[i]); + } +#else + /* assume all enum values are supported untill sai object api is available */ + for (size_t i = 0; i < meta->enummetadata->valuescount; i++) + { + m_aclEnumActionCapabilities[acl_action].insert(meta->enummetadata->values[i]); + } +#endif + + // put supported values in DB + for (const auto& it: lookupMap) + { + const auto foundIt = m_aclEnumActionCapabilities[acl_action].find(it.second); + if (foundIt == m_aclEnumActionCapabilities[acl_action].cend()) + { + continue; + } + acl_action_value_stream << delimiter << it.first; + delimiter = comma; + } + + fvVector.emplace_back(field, acl_action_value_stream.str()); + } + + m_switchTable.set("switch", fvVector); +} + +sai_acl_action_type_t AclOrch::getAclActionFromAclEntry(sai_acl_entry_attr_t attr) +{ + if (attr < SAI_ACL_ENTRY_ATTR_ACTION_START || attr > SAI_ACL_ENTRY_ATTR_ACTION_END) + { + SWSS_LOG_THROW("Invalid ACL entry attribute passed in: %d", attr); + } + + return static_cast(attr - SAI_ACL_ENTRY_ATTR_ACTION_START); +}; + AclOrch::AclOrch(vector& connectors, TableConnector switchTable, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *dtelOrch) : Orch(connectors), @@ -2128,7 +2400,7 @@ void AclOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -2207,7 +2479,7 @@ bool AclOrch::addAclTable(AclTable &newTable, string table_id) if (createBindAclTable(newTable, table_oid)) { m_AclTables[table_oid] = newTable; - SWSS_LOG_NOTICE("Created ACL table %s oid:%lx", + SWSS_LOG_NOTICE("Created ACL table %s oid:%" PRIx64, newTable.id.c_str(), table_oid); // Mark the existence of the mirror table @@ -2310,6 +2582,26 @@ bool AclOrch::isCombinedMirrorV6Table() return m_isCombinedMirrorV6Table; } +bool AclOrch::isAclActionSupported(acl_stage_type_t stage, sai_acl_action_type_t action) const +{ + const auto& it = m_aclCapabilities.find(stage); + if (it == m_aclCapabilities.cend()) + { + return false; + } + return it->second.find(action) != it->second.cend(); +} + +bool AclOrch::isAclActionEnumValueSupported(sai_acl_action_type_t action, sai_acl_action_parameter_t param) const +{ + const auto& it = m_aclEnumActionCapabilities.find(action); + if (it == m_aclEnumActionCapabilities.cend()) + { + return false; + } + return it->second.find(param.s32) != it->second.cend(); +} + void AclOrch::doAclTableTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -2648,6 +2940,16 @@ sai_object_id_t AclOrch::getTableById(string table_id) return SAI_NULL_OBJECT_ID; } +const AclTable *AclOrch::getTableByOid(sai_object_id_t oid) const +{ + const auto& it = m_AclTables.find(oid); + if (it == m_AclTables.cend()) + { + return nullptr; + } + return &it->second; +} + bool AclOrch::createBindAclTable(AclTable &aclTable, sai_object_id_t &table_oid) { SWSS_LOG_ENTER(); @@ -2698,7 +3000,8 @@ void AclOrch::doTask(SelectableTimer &timer) swss::FieldValueTuple fvtb("Bytes", to_string(cnt.bytes)); values.push_back(fvtb); - AclOrch::getCountersTable().set(table_it.second.id + ":" + rule_it.second->getId(), values, ""); + AclOrch::getCountersTable().set(rule_it.second->getTableId() + ":" + + rule_it.second->getId(), values, ""); } values.clear(); } @@ -2820,7 +3123,7 @@ sai_status_t AclOrch::createDTelWatchListTables() gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, SAI_ACL_STAGE_INGRESS, SAI_ACL_BIND_POINT_TYPE_SWITCH); m_AclTables[table_oid] = flowWLTable; - SWSS_LOG_INFO("Successfully created ACL table %s, oid: %lX", flowWLTable.description.c_str(), table_oid); + SWSS_LOG_INFO("Successfully created ACL table %s, oid: %" PRIx64, flowWLTable.description.c_str(), table_oid); /* Create Drop watchlist ACL table */ @@ -2881,7 +3184,7 @@ sai_status_t AclOrch::createDTelWatchListTables() gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, SAI_ACL_STAGE_INGRESS, SAI_ACL_BIND_POINT_TYPE_SWITCH); m_AclTables[table_oid] = dropWLTable; - SWSS_LOG_INFO("Successfully created ACL table %s, oid: %lX", dropWLTable.description.c_str(), table_oid); + SWSS_LOG_INFO("Successfully created ACL table %s, oid: %" PRIx64, dropWLTable.description.c_str(), table_oid); return status; } diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 558a0326454..45cfd675e7c 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -62,7 +62,10 @@ #define MATCH_INNER_L4_DST_PORT "INNER_L4_DST_PORT" #define ACTION_PACKET_ACTION "PACKET_ACTION" +#define ACTION_REDIRECT_ACTION "REDIRECT_ACTION" #define ACTION_MIRROR_ACTION "MIRROR_ACTION" +#define ACTION_MIRROR_INGRESS_ACTION "MIRROR_INGRESS_ACTION" +#define ACTION_MIRROR_EGRESS_ACTION "MIRROR_EGRESS_ACTION" #define ACTION_DTEL_FLOW_OP "FLOW_OP" #define ACTION_DTEL_INT_SESSION "INT_SESSION" #define ACTION_DTEL_DROP_REPORT_ENABLE "DROP_REPORT_ENABLE" @@ -113,7 +116,10 @@ typedef map acl_table_type_lookup_t; typedef map acl_rule_attr_lookup_t; typedef map acl_ip_type_lookup_t; typedef map acl_dtel_flow_op_type_lookup_t; +typedef map acl_packet_action_lookup_t; typedef tuple acl_range_properties_t; +typedef map> acl_capabilities_t; +typedef map> acl_action_enum_values_capabilities_t; class AclOrch; @@ -170,7 +176,7 @@ class AclRule AclRule(AclOrch *m_pAclOrch, string rule, string table, acl_table_type_t type, bool createCounter = true); virtual bool validateAddPriority(string attr_name, string attr_value); virtual bool validateAddMatch(string attr_name, string attr_value); - virtual bool validateAddAction(string attr_name, string attr_value) = 0; + virtual bool validateAddAction(string attr_name, string attr_value); virtual bool validate() = 0; bool processIpType(string type, sai_uint32_t &ip_type); inline static void setRulePriorities(sai_uint32_t min, sai_uint32_t max) @@ -209,6 +215,8 @@ class AclRule void decreaseNextHopRefCount(); + bool isActionSupported(sai_acl_entry_attr_t) const; + static sai_uint32_t m_minPriority; static sai_uint32_t m_maxPriority; AclOrch *m_pAclOrch; @@ -272,10 +280,10 @@ class AclRuleMirror: public AclRule AclRuleCounters getCounters(); protected: - bool m_state; + bool m_state {false}; string m_sessionName; AclRuleCounters counters; - MirrorOrch *m_pMirrorOrch; + MirrorOrch *m_pMirrorOrch {nullptr}; }; class AclRuleDTelFlowWatchListEntry: public AclRule @@ -386,6 +394,7 @@ class AclOrch : public Orch, public Observer void update(SubjectType, void *); sai_object_id_t getTableById(string table_id); + const AclTable* getTableByOid(sai_object_id_t oid) const; static swss::Table& getCountersTable() { @@ -406,10 +415,14 @@ class AclOrch : public Orch, public Observer bool removeAclRule(string table_id, string rule_id); bool isCombinedMirrorV6Table(); + bool isAclActionSupported(acl_stage_type_t stage, sai_acl_action_type_t action) const; + bool isAclActionEnumValueSupported(sai_acl_action_type_t action, sai_acl_action_parameter_t param) const; bool m_isCombinedMirrorV6Table = true; map m_mirrorTableCapabilities; + static sai_acl_action_type_t getAclActionFromAclEntry(sai_acl_entry_attr_t attr); + private: void doTask(Consumer &consumer); void doAclTableTask(Consumer &consumer); @@ -418,6 +431,12 @@ class AclOrch : public Orch, public Observer void init(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); void queryMirrorTableCapability(); + void queryAclActionCapability(); + + template + void queryAclActionAttrEnumValues(const string& action_name, + const acl_rule_attr_lookup_t& ruleAttrLookupMap, + const AclActionAttrLookupT lookupMap); static void collectCountersThread(AclOrch *pAclOrch); @@ -444,6 +463,9 @@ class AclOrch : public Orch, public Observer string m_mirrorTableId; string m_mirrorV6TableId; + + acl_capabilities_t m_aclCapabilities; + acl_action_enum_values_capabilities_t m_aclEnumActionCapabilities; }; #endif /* SWSS_ACLORCH_H */ diff --git a/orchagent/acltable.h b/orchagent/acltable.h index 097ead33aea..42446788439 100644 --- a/orchagent/acltable.h +++ b/orchagent/acltable.h @@ -14,8 +14,10 @@ using namespace std; /* TODO: move all acltable and aclrule implementation out of aclorch */ -#define TABLE_INGRESS "INGRESS" -#define TABLE_EGRESS "EGRESS" +#define STAGE_INGRESS "INGRESS" +#define STAGE_EGRESS "EGRESS" +#define TABLE_INGRESS STAGE_INGRESS +#define TABLE_EGRESS STAGE_EGRESS #define TABLE_STAGE "STAGE" typedef enum diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index e35b4c7f48d..c560551bca5 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -3,6 +3,7 @@ #include "logger.h" #include "sai_serialize.h" +#include #include #include @@ -110,11 +111,6 @@ void BufferOrch::initFlexCounterGroupTable(void) fvTuples.emplace_back(BUFFER_POOL_PLUGIN_FIELD, bufferPoolWmSha); fvTuples.emplace_back(POLL_INTERVAL_FIELD, BUFFER_POOL_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS); - // TODO (work in progress): - // Some platforms do not support buffer pool watermark clear operation on a particular pool - // Invoke the SAI clear_stats API per pool to query the capability from the API call return status - fvTuples.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ_AND_CLEAR); - m_flexCounterGroupTable->set(BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP, fvTuples); } catch (const runtime_error &e) @@ -170,14 +166,60 @@ void BufferOrch::generateBufferPoolWatermarkCounterIdList(void) statList.pop_back(); } - vector fvTuples; - fvTuples.emplace_back(BUFFER_POOL_COUNTER_ID_LIST, statList); + // Some platforms do not support buffer pool watermark clear operation on a particular pool + // Invoke the SAI clear_stats API per pool to query the capability from the API call return status + // We use bit mask to mark the clear watermark capability of each buffer pool. We use an unsigned int to place hold + // these bits. This assumes the total number of buffer pools to be no greater than 32, which should satisfy all use cases. + unsigned int noWmClrCapability = 0; + unsigned int bitMask = 1; + for (const auto &it : *(m_buffer_type_maps[CFG_BUFFER_POOL_TABLE_NAME])) + { + sai_status_t status = sai_buffer_api->clear_buffer_pool_stats( + it.second, + static_cast(bufferPoolWatermarkStatIds.size()), + reinterpret_cast(bufferPoolWatermarkStatIds.data())); + if (status == SAI_STATUS_NOT_SUPPORTED || status == SAI_STATUS_NOT_IMPLEMENTED) + { + SWSS_LOG_NOTICE("Clear watermark failed on %s, rv: %s", it.first.c_str(), sai_serialize_status(status).c_str()); + noWmClrCapability |= bitMask; + } + + bitMask <<= 1; + } + + if (!noWmClrCapability) + { + vector fvs; + + fvs.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ_AND_CLEAR); + m_flexCounterGroupTable->set(BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP, fvs); + } // Push buffer pool watermark COUNTER_ID_LIST to FLEX_COUNTER_TABLE on a per buffer pool basis + vector fvTuples; + fvTuples.emplace_back(BUFFER_POOL_COUNTER_ID_LIST, statList); + bitMask = 1; for (const auto &it : *(m_buffer_type_maps[CFG_BUFFER_POOL_TABLE_NAME])) { string key = BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP ":" + sai_serialize_object_id(it.second); - m_flexCounterTable->set(key, fvTuples); + + if (noWmClrCapability) + { + string stats_mode = STATS_MODE_READ_AND_CLEAR; + if (noWmClrCapability & bitMask) + { + stats_mode = STATS_MODE_READ; + } + fvTuples.emplace_back(STATS_MODE_FIELD, stats_mode); + + m_flexCounterTable->set(key, fvTuples); + fvTuples.pop_back(); + bitMask <<= 1; + } + else + { + m_flexCounterTable->set(key, fvTuples); + } } m_isBufferPoolWatermarkCounterIdListGenerated = true; @@ -280,10 +322,10 @@ task_process_status BufferOrch::processBufferPool(Consumer &consumer) sai_status = sai_buffer_api->set_buffer_pool_attribute(sai_object, &attribs[0]); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to modify buffer pool, name:%s, sai object:%lx, status:%d", object_name.c_str(), sai_object, sai_status); + SWSS_LOG_ERROR("Failed to modify buffer pool, name:%s, sai object:%" PRIx64 ", status:%d", object_name.c_str(), sai_object, sai_status); return task_process_status::task_failed; } - SWSS_LOG_DEBUG("Modified existing pool:%lx, type:%s name:%s ", sai_object, map_type_name.c_str(), object_name.c_str()); + SWSS_LOG_DEBUG("Modified existing pool:%" PRIx64 ", type:%s name:%s ", sai_object, map_type_name.c_str(), object_name.c_str()); } else { @@ -422,11 +464,11 @@ task_process_status BufferOrch::processBufferProfile(Consumer &consumer) } if (SAI_NULL_OBJECT_ID != sai_object) { - SWSS_LOG_DEBUG("Modifying existing sai object:%lx ", sai_object); + SWSS_LOG_DEBUG("Modifying existing sai object:%" PRIx64, sai_object); sai_status = sai_buffer_api->set_buffer_profile_attribute(sai_object, &attribs[0]); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to modify buffer profile, name:%s, sai object:%lx, status:%d", object_name.c_str(), sai_object, sai_status); + SWSS_LOG_ERROR("Failed to modify buffer profile, name:%s, sai object:%" PRIx64 ", status:%d", object_name.c_str(), sai_object, sai_status); return task_process_status::task_failed; } } @@ -521,7 +563,7 @@ task_process_status BufferOrch::processQueue(Consumer &consumer) return task_process_status::task_invalid_entry; } queue_id = port.m_queue_ids[ind]; - SWSS_LOG_DEBUG("Applying buffer profile:0x%lx to queue index:%zd, queue sai_id:0x%lx", sai_buffer_profile, ind, queue_id); + SWSS_LOG_DEBUG("Applying buffer profile:0x%" PRIx64 " to queue index:%zd, queue sai_id:0x%" PRIx64, sai_buffer_profile, ind, queue_id); sai_status_t sai_status = sai_queue_api->set_queue_attribute(queue_id, &attr); if (sai_status != SAI_STATUS_SUCCESS) { @@ -608,7 +650,7 @@ task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) return task_process_status::task_invalid_entry; } pg_id = port.m_priority_group_ids[ind]; - SWSS_LOG_DEBUG("Applying buffer profile:0x%lx to port:%s pg index:%zd, pg sai_id:0x%lx", sai_buffer_profile, port_name.c_str(), ind, pg_id); + SWSS_LOG_DEBUG("Applying buffer profile:0x%" PRIx64 " to port:%s pg index:%zd, pg sai_id:0x%" PRIx64, sai_buffer_profile, port_name.c_str(), ind, pg_id); sai_status_t sai_status = sai_buffer_api->set_ingress_priority_group_attribute(pg_id, &attr); if (sai_status != SAI_STATUS_SUCCESS) { diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index a38aa99a4b8..6e3c711d425 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -4,6 +4,7 @@ #include "tokenize.h" #include "logger.h" +#include #include #include @@ -60,7 +61,7 @@ static map trap_id_map = { {"neigh_discovery", SAI_HOSTIF_TRAP_TYPE_IPV6_NEIGHBOR_DISCOVERY}, {"mld_v1_v2", SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_V2}, {"mld_v1_report", SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_REPORT}, - {"mld_v2_done", SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_DONE}, + {"mld_v1_done", SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_DONE}, {"mld_v2_report", SAI_HOSTIF_TRAP_TYPE_MLD_V2_REPORT}, {"ip2me", SAI_HOSTIF_TRAP_TYPE_IP2ME}, {"ssh", SAI_HOSTIF_TRAP_TYPE_SSH}, @@ -277,12 +278,12 @@ sai_object_id_t CoppOrch::getPolicer(string trap_group_name) { return SAI_NULL_OBJECT_ID; } - SWSS_LOG_DEBUG("trap group id:%lx", m_trap_group_map[trap_group_name]); + SWSS_LOG_DEBUG("trap group id:%" PRIx64, m_trap_group_map[trap_group_name]); if (m_trap_group_policer_map.find(m_trap_group_map[trap_group_name]) == m_trap_group_policer_map.end()) { return SAI_NULL_OBJECT_ID; } - SWSS_LOG_DEBUG("trap group policer id:%lx", m_trap_group_policer_map[m_trap_group_map[trap_group_name]]); + SWSS_LOG_DEBUG("trap group policer id:%" PRIx64, m_trap_group_policer_map[m_trap_group_map[trap_group_name]]); return m_trap_group_policer_map[m_trap_group_map[trap_group_name]]; } @@ -456,12 +457,12 @@ task_process_status CoppOrch::processCoppRule(Consumer& consumer) sai_object_id_t policer_id = getPolicer(trap_group_name); if (SAI_NULL_OBJECT_ID == policer_id) { - SWSS_LOG_WARN("Creating policer for existing Trap group:%lx (name:%s).", m_trap_group_map[trap_group_name], trap_group_name.c_str()); + SWSS_LOG_WARN("Creating policer for existing Trap group:%" PRIx64 " (name:%s).", m_trap_group_map[trap_group_name], trap_group_name.c_str()); if (!createPolicer(trap_group_name, policer_attribs)) { return task_process_status::task_failed; } - SWSS_LOG_DEBUG("Created policer:%lx for existing trap group", policer_id); + SWSS_LOG_DEBUG("Created policer:%" PRIx64 " for existing trap group", policer_id); } else { @@ -487,7 +488,7 @@ task_process_status CoppOrch::processCoppRule(Consumer& consumer) sai_status = sai_hostif_api->set_hostif_trap_group_attribute(m_trap_group_map[trap_group_name], &trap_gr_attr); if (sai_status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to apply attribute:%d to trap group:%lx, name:%s, error:%d\n", trap_gr_attr.id, m_trap_group_map[trap_group_name], trap_group_name.c_str(), sai_status); + SWSS_LOG_ERROR("Failed to apply attribute:%d to trap group:%" PRIx64 ", name:%s, error:%d\n", trap_gr_attr.id, m_trap_group_map[trap_group_name], trap_group_name.c_str(), sai_status); return task_process_status::task_failed; } SWSS_LOG_NOTICE("Set trap group %s to host interface", trap_group_name.c_str()); @@ -589,7 +590,7 @@ void CoppOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } diff --git a/orchagent/countercheckorch.cpp b/orchagent/countercheckorch.cpp index c2d3239bd97..68edb69e204 100644 --- a/orchagent/countercheckorch.cpp +++ b/orchagent/countercheckorch.cpp @@ -4,6 +4,7 @@ #include "notifier.h" #include "redisclient.h" #include "sai_serialize.h" +#include #define COUNTER_CHECK_POLL_TIMEOUT_SEC (5 * 60) @@ -61,7 +62,7 @@ void CounterCheckOrch::mcCounterCheck() Port port; if (!gPortsOrch->getPort(oid, port)) { - SWSS_LOG_ERROR("Invalid port oid 0x%lx", oid); + SWSS_LOG_ERROR("Invalid port oid 0x%" PRIx64, oid); continue; } @@ -78,13 +79,13 @@ void CounterCheckOrch::mcCounterCheck() bool isLossy = ((1 << prio) & pfcMask) == 0; if (newMcCounters[prio] == numeric_limits::max()) { - SWSS_LOG_WARN("Could not retreive MC counters on queue %lu port %s", + SWSS_LOG_WARN("Could not retreive MC counters on queue %zu port %s", prio, port.m_alias.c_str()); } else if (!isLossy && mcCounters[prio] < newMcCounters[prio]) { - SWSS_LOG_WARN("Got Multicast %lu frame(s) on lossless queue %lu port %s", + SWSS_LOG_WARN("Got Multicast %" PRIu64 " frame(s) on lossless queue %zu port %s", newMcCounters[prio] - mcCounters[prio], prio, port.m_alias.c_str()); @@ -109,7 +110,7 @@ void CounterCheckOrch::pfcFrameCounterCheck() Port port; if (!gPortsOrch->getPort(oid, port)) { - SWSS_LOG_ERROR("Invalid port oid 0x%lx", oid); + SWSS_LOG_ERROR("Invalid port oid 0x%" PRIx64, oid); continue; } @@ -124,13 +125,13 @@ void CounterCheckOrch::pfcFrameCounterCheck() bool isLossy = ((1 << prio) & pfcMask) == 0; if (newCounters[prio] == numeric_limits::max()) { - SWSS_LOG_WARN("Could not retreive PFC frame count on queue %lu port %s", + SWSS_LOG_WARN("Could not retreive PFC frame count on queue %zu port %s", prio, port.m_alias.c_str()); } else if (isLossy && counters[prio] < newCounters[prio]) { - SWSS_LOG_WARN("Got PFC %lu frame(s) on lossy queue %lu port %s", + SWSS_LOG_WARN("Got PFC %" PRIu64 " frame(s) on lossy queue %zu port %s", newCounters[prio] - counters[prio], prio, port.m_alias.c_str()); diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 41f68dfc20e..9e26cd58aa1 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -1,4 +1,5 @@ #include +#include #include "crmorch.h" #include "converter.h" @@ -239,7 +240,7 @@ void CrmOrch::handleSetCommand(const string& key, const vector& if (field == CRM_POLLING_INTERVAL) { m_pollingInterval = chrono::seconds(to_uint(value)); - auto interv = timespec { .tv_sec = m_pollingInterval.count(), .tv_nsec = 0 }; + auto interv = timespec { .tv_sec = (time_t)m_pollingInterval.count(), .tv_nsec = 0 }; m_timer->setInterval(interv); m_timer->reset(); } @@ -378,7 +379,7 @@ void CrmOrch::incCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_ } catch (...) { - SWSS_LOG_ERROR("Failed to increment \"used\" counter for the %s CRM resource (tableId:%lx).", crmResTypeNameMap.at(resource).c_str(), tableId); + SWSS_LOG_ERROR("Failed to increment \"used\" counter for the %s CRM resource (tableId:%" PRIx64 ").", crmResTypeNameMap.at(resource).c_str(), tableId); return; } } @@ -393,7 +394,7 @@ void CrmOrch::decCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_ } catch (...) { - SWSS_LOG_ERROR("Failed to decrement \"used\" counter for the %s CRM resource (tableId:%lx).", crmResTypeNameMap.at(resource).c_str(), tableId); + SWSS_LOG_ERROR("Failed to decrement \"used\" counter for the %s CRM resource (tableId:%" PRIx64 ").", crmResTypeNameMap.at(resource).c_str(), tableId); return; } } diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 7498eb7e055..2c0ff988df8 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "logger.h" #include "tokenize.h" @@ -63,7 +64,7 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) if (!m_portsOrch->getPort(entry.bv_id, vlan)) { - SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan port from bv_id 0x%lx", entry.bv_id); + SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry.bv_id); return false; } @@ -74,7 +75,7 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) { auto inserted = m_entries.insert(entry); - SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into bv_id 0x%lx", + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); if (!inserted.second) @@ -95,7 +96,7 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) else { size_t erased = m_entries.erase(entry); - SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from bv_id 0x%lx", entry.mac.to_string().c_str(), entry.bv_id); + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); if (erased == 0) { @@ -123,14 +124,14 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj case SAI_FDB_EVENT_LEARNED: if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%lx", bridge_port_id); + SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); return; } // we already have such entries if (m_entries.find(update.entry) != m_entries.end()) { - SWSS_LOG_INFO("FdbOrch notification: mac %s is already in bv_id 0x%lx", + SWSS_LOG_INFO("FdbOrch notification: mac %s is already in bv_id 0x%" PRIx64, update.entry.mac.to_string().c_str(), entry->bv_id); break; } @@ -186,16 +187,16 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj else if (bridge_port_id && entry->bv_id == SAI_NULL_OBJECT_ID) { /*this is a placeholder for flush port fdb case, not supported yet.*/ - SWSS_LOG_ERROR("FdbOrch notification: not supported flush port fdb action, port_id = 0x%lx, bv_id = 0x%lx.", bridge_port_id, entry->bv_id); + SWSS_LOG_ERROR("FdbOrch notification: not supported flush port fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); } else if (bridge_port_id == SAI_NULL_OBJECT_ID && entry->bv_id != SAI_NULL_OBJECT_ID) { /*this is a placeholder for flush vlan fdb case, not supported yet.*/ - SWSS_LOG_ERROR("FdbOrch notification: not supported flush vlan fdb action, port_id = 0x%lx, bv_id = 0x%lx.", bridge_port_id, entry->bv_id); + SWSS_LOG_ERROR("FdbOrch notification: not supported flush vlan fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); } else { - SWSS_LOG_ERROR("FdbOrch notification: not supported flush fdb action, port_id = 0x%lx, bv_id = 0x%lx.", bridge_port_id, entry->bv_id); + SWSS_LOG_ERROR("FdbOrch notification: not supported flush fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); } break; } @@ -251,7 +252,7 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) if (!m_portsOrch->getPortByBridgePortId(attr.value.oid, port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%lx", attr.value.oid); + SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, attr.value.oid); return false; } @@ -262,7 +263,7 @@ void FdbOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -342,7 +343,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -441,14 +442,6 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const { SWSS_LOG_ENTER(); - if (m_entries.count(entry) != 0) // we already have such entries - { - // FIXME: should we check that the entry are moving to another port? - // FIXME: should we check that the entry are changing its type? - SWSS_LOG_ERROR("FDB entry already exists. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); - return true; - } - sai_fdb_entry_t fdb_entry; fdb_entry.switch_id = gSwitchId; @@ -489,6 +482,11 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const attr.value.s32 = SAI_PACKET_ACTION_FORWARD; attrs.push_back(attr); + if (m_entries.count(entry) != 0) // we already have such entries + { + removeFdbEntry(entry); + } + sai_status_t status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -518,7 +516,7 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) if (m_entries.count(entry) == 0) { - SWSS_LOG_ERROR("FDB entry isn't found. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); + SWSS_LOG_ERROR("FDB entry isn't found. mac=%s bv_id=0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); return true; } @@ -531,7 +529,7 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) status = sai_fdb_api->remove_fdb_entry(&fdb_entry); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, bv_id=0x%lx", + SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, bv_id=0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); return true; //FIXME: it should be based on status. Some could be retried. some not } diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 1e057bfb5b4..8493f6897a3 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -45,7 +45,7 @@ void FlexCounterOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index d9946fa981f..c36e6757c91 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "sai_serialize.h" #include "intfsorch.h" @@ -252,7 +253,7 @@ void IntfsOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -856,7 +857,7 @@ void IntfsOrch::doTask(SelectableTimer &timer) { SWSS_LOG_ENTER(); - SWSS_LOG_DEBUG("Registering %ld new intfs", m_rifsToAdd.size()); + SWSS_LOG_DEBUG("Registering %" PRId64 " new intfs", m_rifsToAdd.size()); string value; for (auto it = m_rifsToAdd.begin(); it != m_rifsToAdd.end(); ) { diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 856e3a02a3d..a0bb8af5245 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -12,6 +12,7 @@ extern "C" { #include #include #include +#include #include #include "timestamp.h" @@ -46,12 +47,14 @@ int gBatchSize = DEFAULT_BATCH_SIZE; bool gSairedisRecord = true; bool gSwssRecord = true; bool gLogRotate = false; +bool gSyncMode = false; + ofstream gRecordOfs; string gRecordFile; void usage() { - cout << "usage: orchagent [-h] [-r record_type] [-d record_location] [-b batch_size] [-m MAC]" << endl; + cout << "usage: orchagent [-h] [-r record_type] [-d record_location] [-b batch_size] [-m MAC] [-s]" << endl; cout << " -h: display this message" << endl; cout << " -r record_type: record orchagent logs with type (default 3)" << endl; cout << " 0: do not record logs" << endl; @@ -61,6 +64,7 @@ void usage() cout << " -d record_location: set record logs folder location (default .)" << endl; cout << " -b batch_size: set consumer table pop operation batch size (default 128)" << endl; cout << " -m MAC: set switch MAC address" << endl; + cout << " -s: enable synchronous mode" << endl; } void sighup_handler(int signo) @@ -117,7 +121,7 @@ int main(int argc, char **argv) string record_location = "."; - while ((opt = getopt(argc, argv, "b:m:r:d:h")) != -1) + while ((opt = getopt(argc, argv, "b:m:r:d:hs")) != -1) { switch (opt) { @@ -162,6 +166,11 @@ int main(int argc, char **argv) case 'h': usage(); exit(EXIT_SUCCESS); + case 's': + gSyncMode = true; + SWSS_LOG_NOTICE("Enabling synchronous mode"); + break; + default: /* '?' */ exit(EXIT_FAILURE); } @@ -211,6 +220,18 @@ int main(int argc, char **argv) attrs.push_back(attr); } + // SAI_REDIS_SWITCH_ATTR_SYNC_MODE attribute only setBuffer and g_syncMode to true + // since it is not using ASIC_DB, we can execute it before create_switch + // when g_syncMode is set to true here, create_switch will wait the response from syncd + if (gSyncMode) + { + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_MODE; + attr.value.booldata = true; + + sai_switch_api->set_switch_attribute(gSwitchId, &attr); + } + + status = sai_switch_api->create_switch(&gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -246,7 +267,7 @@ int main(int argc, char **argv) } gVirtualRouterId = attr.value.oid; - SWSS_LOG_NOTICE("Get switch virtual router ID %lx", gVirtualRouterId); + SWSS_LOG_NOTICE("Get switch virtual router ID %" PRIx64, gVirtualRouterId); /* Create a loopback underlay router interface */ vector underlay_intf_attrs; @@ -267,7 +288,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - SWSS_LOG_NOTICE("Created underlay router interface ID %lx", gUnderlayIfId); + SWSS_LOG_NOTICE("Created underlay router interface ID %" PRIx64, gUnderlayIfId); /* Initialize orchestration components */ DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 25b798603c9..91b07aa81a9 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -10,10 +10,12 @@ #include "swssnet.h" #include "converter.h" #include "mirrororch.h" +#include "tokenize.h" #define MIRROR_SESSION_STATUS "status" #define MIRROR_SESSION_STATUS_ACTIVE "active" #define MIRROR_SESSION_STATUS_INACTIVE "inactive" +#define MIRROR_SESSION_NEXT_HOP_IP "next_hop_ip" #define MIRROR_SESSION_SRC_IP "src_ip" #define MIRROR_SESSION_DST_IP "dst_ip" #define MIRROR_SESSION_GRE_TYPE "gre_type" @@ -23,7 +25,7 @@ #define MIRROR_SESSION_DST_MAC_ADDRESS "dst_mac" #define MIRROR_SESSION_MONITOR_PORT "monitor_port" #define MIRROR_SESSION_ROUTE_PREFIX "route_prefix" -#define MIRROR_SESSION_VLAN_HEADER_VALID "vlan_header_valid" +#define MIRROR_SESSION_VLAN_ID "vlan_id" #define MIRROR_SESSION_POLICER "policer" #define MIRROR_SESSION_DEFAULT_VLAN_PRI 0 @@ -58,6 +60,7 @@ MirrorEntry::MirrorEntry(const string& platform) : } nexthopInfo.prefix = IpPrefix("0.0.0.0/0"); + nexthopInfo.nexthop = IpAddress("0.0.0.0"); } MirrorOrch::MirrorOrch(TableConnector stateDbConnector, TableConnector confDbConnector, @@ -75,6 +78,74 @@ MirrorOrch::MirrorOrch(TableConnector stateDbConnector, TableConnector confDbCon m_fdbOrch->attach(this); } +bool MirrorOrch::bake() +{ + SWSS_LOG_ENTER(); + + // Freeze the route update during orchagent restoration + m_freeze = true; + + deque entries; + vector keys; + m_mirrorTable.getKeys(keys); + for (const auto &key : keys) + { + vector tuples; + m_mirrorTable.get(key, tuples); + + bool active = false; + string monitor_port; + string next_hop_ip; + + for (const auto &tuple : tuples) + { + if (fvField(tuple) == MIRROR_SESSION_STATUS) + { + active = fvValue(tuple) == MIRROR_SESSION_STATUS_ACTIVE; + } + else if (fvField(tuple) == MIRROR_SESSION_MONITOR_PORT) + { + monitor_port = fvValue(tuple); + } + else if (fvField(tuple) == MIRROR_SESSION_NEXT_HOP_IP) + { + next_hop_ip = fvValue(tuple); + } + } + + if (active) + { + SWSS_LOG_NOTICE("Found mirror session %s active before warm reboot", + key.c_str()); + + // Recover saved active session's monitor port + m_recoverySessionMap.emplace( + key, monitor_port + state_db_key_delimiter + next_hop_ip); + } + + removeSessionState(key); + } + + return Orch::bake(); +} + +bool MirrorOrch::postBake() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("Start MirrorOrch post-baking"); + + // Unfreeze the route update + m_freeze = false; + + Orch::doTask(); + + // Clean up the recovery cache + m_recoverySessionMap.clear(); + + return Orch::postBake(); +} + void MirrorOrch::update(SubjectType type, void *cntx) { SWSS_LOG_ENTER(); @@ -311,6 +382,8 @@ void MirrorOrch::deleteEntry(const string& name) m_policerOrch->decreaseRefCount(session.policer); } + removeSessionState(name); + m_syncdMirrors.erase(sessionIter); SWSS_LOG_NOTICE("Removed mirror session %s", name.c_str()); @@ -320,10 +393,11 @@ void MirrorOrch::setSessionState(const string& name, const MirrorEntry& session, { SWSS_LOG_ENTER(); - SWSS_LOG_INFO("Setting mirroring sessions %s state\n", name.c_str()); + SWSS_LOG_INFO("Update mirroring sessions %s state", name.c_str()); vector fvVector; string value; + if (attr.empty() || attr == MIRROR_SESSION_STATUS) { value = session.status ? MIRROR_SESSION_STATUS_ACTIVE : MIRROR_SESSION_STATUS_INACTIVE; @@ -332,8 +406,9 @@ void MirrorOrch::setSessionState(const string& name, const MirrorEntry& session, if (attr.empty() || attr == MIRROR_SESSION_MONITOR_PORT) { - value = sai_serialize_object_id(session.neighborInfo.portId); - fvVector.emplace_back(MIRROR_SESSION_MONITOR_PORT, value); + Port port; + m_portsOrch->getPort(session.neighborInfo.portId, port); + fvVector.emplace_back(MIRROR_SESSION_MONITOR_PORT, port.m_alias); } if (attr.empty() || attr == MIRROR_SESSION_DST_MAC_ADDRESS) @@ -348,15 +423,28 @@ void MirrorOrch::setSessionState(const string& name, const MirrorEntry& session, fvVector.emplace_back(MIRROR_SESSION_ROUTE_PREFIX, value); } - if (attr.empty() || attr == MIRROR_SESSION_VLAN_HEADER_VALID) + if (attr.empty() || attr == MIRROR_SESSION_VLAN_ID) + { + value = to_string(session.neighborInfo.port.m_vlan_info.vlan_id); + fvVector.emplace_back(MIRROR_SESSION_VLAN_ID, value); + } + + if (attr.empty() || attr == MIRROR_SESSION_NEXT_HOP_IP) { - value = to_string(session.neighborInfo.port.m_type == Port::VLAN); - fvVector.emplace_back(MIRROR_SESSION_VLAN_HEADER_VALID, value); + value = session.nexthopInfo.nexthop.to_string(); + fvVector.emplace_back(MIRROR_SESSION_NEXT_HOP_IP, value); } m_mirrorTable.set(name, fvVector); } +void MirrorOrch::removeSessionState(const string& name) +{ + SWSS_LOG_ENTER(); + + m_mirrorTable.del(name); +} + bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) { SWSS_LOG_ENTER(); @@ -396,32 +484,68 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) return false; } - // Get the firt member of the LAG - Port member; - const auto& first_member_alias = *session.neighborInfo.port.m_members.begin(); - m_portsOrch->getPort(first_member_alias, member); + // Recover the LAG member monitor port picked before warm reboot + // to minimalize the data plane changes across warm reboot. + if (m_recoverySessionMap.find(name) != m_recoverySessionMap.end()) + { + string alias = tokenize(m_recoverySessionMap[name], + state_db_key_delimiter, 1)[0]; + Port member; + m_portsOrch->getPort(alias, member); + + SWSS_LOG_NOTICE("Recover mirror session %s with LAG member port %s", + name.c_str(), alias.c_str()); + session.neighborInfo.portId = member.m_port_id; + } + else + { + // Get the firt member of the LAG + Port member; + string first_member_alias = *session.neighborInfo.port.m_members.begin(); + m_portsOrch->getPort(first_member_alias, member); + + session.neighborInfo.portId = member.m_port_id; + } - session.neighborInfo.portId = member.m_port_id; return true; } case Port::VLAN: { SWSS_LOG_NOTICE("Get mirror session destination IP neighbor VLAN %d", session.neighborInfo.port.m_vlan_info.vlan_id); - Port member; - if (!m_fdbOrch->getPort(session.neighborInfo.mac, session.neighborInfo.port.m_vlan_info.vlan_id, member)) + + // Recover the VLAN member monitor port picked before warm reboot + // since the FDB entries are not yet learned on the hardware + if (m_recoverySessionMap.find(name) != m_recoverySessionMap.end()) { - SWSS_LOG_NOTICE("Waiting to get FDB entry MAC %s under VLAN %s", - session.neighborInfo.mac.to_string().c_str(), - session.neighborInfo.port.m_alias.c_str()); - return false; + string alias = tokenize(m_recoverySessionMap[name], + state_db_key_delimiter, 1)[0]; + Port member; + m_portsOrch->getPort(alias, member); + + SWSS_LOG_NOTICE("Recover mirror session %s with VLAN member port %s", + name.c_str(), alias.c_str()); + session.neighborInfo.portId = member.m_port_id; } else { - // Update monitor port - session.neighborInfo.portId = member.m_port_id; - return true; + Port member; + if (!m_fdbOrch->getPort(session.neighborInfo.mac, + session.neighborInfo.port.m_vlan_info.vlan_id, member)) + { + SWSS_LOG_NOTICE("Waiting to get FDB entry MAC %s under VLAN %s", + session.neighborInfo.mac.to_string().c_str(), + session.neighborInfo.port.m_alias.c_str()); + return false; + } + else + { + // Update monitor port + session.neighborInfo.portId = member.m_port_id; + } } + + return true; } default: { @@ -741,7 +865,7 @@ bool MirrorOrch::updateSessionType(const string& name, MirrorEntry& session) SWSS_LOG_NOTICE("Update mirror session %s VLAN to %s", name.c_str(), session.neighborInfo.port.m_alias.c_str()); - setSessionState(name, session, MIRROR_SESSION_VLAN_HEADER_VALID); + setSessionState(name, session, MIRROR_SESSION_VLAN_ID); return true; } @@ -782,7 +906,35 @@ void MirrorOrch::updateNextHop(const NextHopUpdate& update) if (update.nexthopGroup != IpAddresses()) { - session.nexthopInfo.nexthop = *update.nexthopGroup.getIpAddresses().begin(); + SWSS_LOG_NOTICE(" next hop IPs: %s", update.nexthopGroup.to_string().c_str()); + + // Recover the session based on the state database information + if (m_recoverySessionMap.find(name) != m_recoverySessionMap.end()) + { + IpAddress nexthop = IpAddress(tokenize(m_recoverySessionMap[name], + state_db_key_delimiter, 1)[1]); + + // Check if recovered next hop IP is within the update's next hop IPs + if (update.nexthopGroup.getIpAddresses().count(nexthop)) + { + SWSS_LOG_NOTICE("Recover mirror session %s with next hop %s", + name.c_str(), nexthop.to_string().c_str()); + session.nexthopInfo.nexthop = nexthop; + } + else + { + // Correct the next hop IP + SWSS_LOG_NOTICE("Correct mirror session %s next hop from %s to %s", + name.c_str(), session.nexthopInfo.nexthop.to_string().c_str(), + nexthop.to_string().c_str()); + session.nexthopInfo.nexthop = *update.nexthopGroup.getIpAddresses().begin(); + } + } + else + { + // Pick the first one from the next hop group + session.nexthopInfo.nexthop = *update.nexthopGroup.getIpAddresses().begin(); + } } else { @@ -968,7 +1120,12 @@ void MirrorOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (m_freeze) + { + return; + } + + if (!gPortsOrch->allPortsReady()) { return; } @@ -991,7 +1148,7 @@ void MirrorOrch::doTask(Consumer& consumer) } else { - SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); } consumer.m_toSync.erase(it++); diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index 765b24cb5f5..1635012a180 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -69,6 +69,8 @@ class MirrorOrch : public Orch, public Observer, public Subject MirrorOrch(TableConnector appDbConnector, TableConnector confDbConnector, PortsOrch *portOrch, RouteOrch *routeOrch, NeighOrch *neighOrch, FdbOrch *fdbOrch, PolicerOrch *policerOrch); + bool bake() override; + bool postBake() override; void update(SubjectType, void *); bool sessionExists(const string&); bool getSessionStatus(const string&, bool&); @@ -86,6 +88,10 @@ class MirrorOrch : public Orch, public Observer, public Subject Table m_mirrorTable; MirrorTable m_syncdMirrors; + // session_name -> VLAN | monitor_port_alias | next_hop_ip + map m_recoverySessionMap; + + bool m_freeze = false; void createEntry(const string&, const vector&); void deleteEntry(const string&); @@ -102,6 +108,8 @@ class MirrorOrch : public Orch, public Observer, public Subject * attr is the field name will be stored, if empty then all fields will be stored */ void setSessionState(const std::string& name, const MirrorEntry& session, const std::string& attr = ""); + void removeSessionState(const std::string& name); + bool getNeighborInfo(const string&, MirrorEntry&); void updateNextHop(const NextHopUpdate&); diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 1eabb8e3aa0..f4168418008 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -275,7 +275,7 @@ void NeighOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index fb8a8eec5db..a6dce7efbb0 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "timestamp.h" #include "orch.h" @@ -26,7 +27,7 @@ Orch::Orch(DBConnector *db, const string tableName, int pri) Orch::Orch(DBConnector *db, const vector &tableNames) { - for(auto it : tableNames) + for (auto it : tableNames) { addConsumer(db, it, default_orch_pri); } @@ -34,7 +35,7 @@ Orch::Orch(DBConnector *db, const vector &tableNames) Orch::Orch(DBConnector *db, const vector &tableNames_with_pri) { - for(const auto& it : tableNames_with_pri) + for (const auto& it : tableNames_with_pri) { addConsumer(db, it.first, it.second); } @@ -59,7 +60,7 @@ Orch::~Orch() vector Orch::getSelectables() { vector selectables; - for(auto& it : m_consumerMap) + for (auto& it : m_consumerMap) { selectables.push_back(it.second.get()); } @@ -239,7 +240,7 @@ bool Orch::bake() { SWSS_LOG_ENTER(); - for(auto &it : m_consumerMap) + for (auto &it : m_consumerMap) { string executorName = it.first; auto executor = it.second; @@ -256,6 +257,13 @@ bool Orch::bake() return true; } +bool Orch::postBake() +{ + SWSS_LOG_ENTER(); + + return true; +} + /* - Validates reference has proper format which is [table_name:object_name] - validates table_name exists @@ -364,7 +372,7 @@ ref_resolve_status Orch::resolveFieldRefValue( void Orch::doTask() { - for(auto &it : m_consumerMap) + for (auto &it : m_consumerMap) { it.second->drain(); } @@ -372,7 +380,7 @@ void Orch::doTask() void Orch::dumpPendingTasks(vector &ts) { - for(auto &it : m_consumerMap) + for (auto &it : m_consumerMap) { Consumer* consumer = dynamic_cast(it.second.get()); if (consumer == NULL) @@ -462,7 +470,7 @@ ref_resolve_status Orch::resolveFieldRefArray( return ref_resolve_status::not_resolved; } sai_object_id_t sai_obj = (*(type_maps[ref_type_name]))[object_name]; - SWSS_LOG_DEBUG("Resolved to sai_object:0x%lx, type:%s, name:%s", sai_obj, ref_type_name.c_str(), object_name.c_str()); + SWSS_LOG_DEBUG("Resolved to sai_object:0x%" PRIx64 ", type:%s, name:%s", sai_obj, ref_type_name.c_str(), object_name.c_str()); sai_object_arr.push_back(sai_obj); } count++; diff --git a/orchagent/orch.h b/orchagent/orch.h index 83e816a1482..8b2a1308237 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -32,6 +32,7 @@ const char range_specifier = '-'; const char config_db_key_delimiter = '|'; const char state_db_key_delimiter = '|'; +#define INVM_PLATFORM_SUBSTRING "innovium" #define MLNX_PLATFORM_SUBSTRING "mellanox" #define BRCM_PLATFORM_SUBSTRING "broadcom" #define BFN_PLATFORM_SUBSTRING "barefoot" @@ -81,7 +82,7 @@ class Executor : public Selectable // Decorating Selectable int getFd() override { return m_selectable->getFd(); } - void readData() override { m_selectable->readData(); } + uint64_t readData() override { return m_selectable->readData(); } bool hasCachedData() override { return m_selectable->hasCachedData(); } bool initializedWithData() override { return m_selectable->initializedWithData(); } void updateAfterRead() override { m_selectable->updateAfterRead(); } @@ -182,6 +183,8 @@ class Orch // Prepare for warm start if Redis contains valid input data // otherwise fallback to cold start virtual bool bake(); + // Clean up the state set in bake() + virtual bool postBake(); /* Iterate all consumers in m_consumerMap and run doTask(Consumer) */ virtual void doTask(); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index de66476d501..400d24f85a9 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -45,13 +45,13 @@ OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector * OrchDaemon::~OrchDaemon() { SWSS_LOG_ENTER(); - + /* * Some orchagents call other agents in their destructor. * To avoid accessing deleted agent, do deletion in reverse order. * NOTE: This is stil not a robust solution, as order in this list - * does not strictly match the order of construction of agents. - * For a robust solution, first some cleaning/house-keeping in + * does not strictly match the order of construction of agents. + * For a robust solution, first some cleaning/house-keeping in * orchagents management is in order. * For now it fixes, possible crash during process exit. */ @@ -129,6 +129,7 @@ bool OrchDaemon::init() CFG_TC_TO_QUEUE_MAP_TABLE_NAME, CFG_SCHEDULER_TABLE_NAME, CFG_DSCP_TO_TC_MAP_TABLE_NAME, + CFG_DOT1P_TO_TC_MAP_TABLE_NAME, CFG_QUEUE_TABLE_NAME, CFG_PORT_QOS_MAP_TABLE_NAME, CFG_WRED_PROFILE_TABLE_NAME, @@ -179,7 +180,7 @@ bool OrchDaemon::init() /* * The order of the orch list is important for state restore of warm start and - * the queued processing in m_toSync map after gPortsOrch->isPortReady() is set. + * the queued processing in m_toSync map after gPortsOrch->allPortsReady() is set. * * For the multiple consumers in ports_tables, tasks for LAG_TABLE is processed before VLAN_TABLE * when iterating ConsumerMap. @@ -245,6 +246,7 @@ bool OrchDaemon::init() }; if ((platform == MLNX_PLATFORM_SUBSTRING) + || (platform == INVM_PLATFORM_SUBSTRING) || (platform == BFN_PLATFORM_SUBSTRING) || (platform == NPS_PLATFORM_SUBSTRING)) { @@ -278,6 +280,7 @@ bool OrchDaemon::init() static const vector queueAttrIds; if ((platform == MLNX_PLATFORM_SUBSTRING) + || (platform == INVM_PLATFORM_SUBSTRING) || (platform == NPS_PLATFORM_SUBSTRING)) { m_orchList.push_back(new PfcWdSwOrch( @@ -476,14 +479,22 @@ bool OrchDaemon::warmRestoreAndSyncUp() * Fourth iteration: Drain remaining data that are out of order like LAG_MEMBER_TABLE and * VLAN_MEMBER_TABLE since they were checked before LAG_TABLE and VLAN_TABLE within gPortsOrch. */ + for (auto it = 0; it < 4; it++) { + SWSS_LOG_DEBUG("The current iteration is %d", it); + for (Orch *o : m_orchList) { o->doTask(); } } + for (Orch *o : m_orchList) + { + o->postBake(); + } + /* * At this point, all the pre-existing data should have been processed properly, and * orchagent should be in exact same state of pre-shutdown. diff --git a/orchagent/pfc_detect_innovium.lua b/orchagent/pfc_detect_innovium.lua new file mode 100644 index 00000000000..d90f4d5a15b --- /dev/null +++ b/orchagent/pfc_detect_innovium.lua @@ -0,0 +1,111 @@ +-- KEYS - queue IDs +-- ARGV[1] - counters db index +-- ARGV[2] - counters table name +-- ARGV[3] - poll time interval +-- return queue Ids that satisfy criteria + +local counters_db = ARGV[1] +local counters_table_name = ARGV[2] +local poll_time = tonumber(ARGV[3]) + +local rets = {} + +redis.call('SELECT', counters_db) + +-- Iterate through each queue +local n = table.getn(KEYS) +for i = n, 1, -1 do + local counter_keys = redis.call('HKEYS', counters_table_name .. ':' .. KEYS[i]) + local counter_num = 0 + local old_counter_num = 0 + local is_deadlock = false + local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') + local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') + + local big_red_switch_mode = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'BIG_RED_SWITCH_MODE') + if not big_red_switch_mode and (pfc_wd_status == 'operational' or pfc_wd_action == 'alert') then + local detection_time = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME') + if detection_time then + detection_time = tonumber(detection_time) + local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') + if not time_left then + time_left = detection_time + else + time_left = tonumber(time_left) + end + + local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) + local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) + local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' + local pfc_duration_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PAUSE_DURATION' + + -- Get all counters + local occupancy_bytes = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES') + local packets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS') + local pfc_rx_packets = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key) + local pfc_duration = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key) + + if occupancy_bytes and packets and pfc_rx_packets and pfc_duration then + occupancy_bytes = tonumber(occupancy_bytes) + packets = tonumber(packets) + pfc_rx_packets = tonumber(pfc_rx_packets) + pfc_duration = tonumber(pfc_duration) + + local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') + local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') + local pfc_duration_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') + -- DEBUG CODE START. Uncomment to enable + local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') + -- DEBUG CODE END. + + -- If this is not a first run, then we have last values available + if packets_last and pfc_rx_packets_last and pfc_duration_last then + packets_last = tonumber(packets_last) + pfc_rx_packets_last = tonumber(pfc_rx_packets_last) + pfc_duration_last = tonumber(pfc_duration_last) + + -- Check actual condition of queue being in PFC storm + -- if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) then + -- redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'K7_debug_1', 'YES') + + -- if (debug_storm == "enabled") then + -- redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'K7_debug_2', 'YES') + + -- if (occupancy_bytes == 0 and packets - packets_last == 0 and (pfc_duration - pfc_duration_last) > poll_time * 0.8) then + -- redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'K7_debug_3', 'YES') + + + if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or + -- DEBUG CODE START. Uncomment to enable + (debug_storm == "enabled") or + -- DEBUG CODE END. + (occupancy_bytes == 0 and pfc_rx_packets - pfc_rx_packets_last > 0 and (pfc_duration - pfc_duration_last) > poll_time * 0.8) then + if time_left <= poll_time then + redis.call('PUBLISH', 'PFC_WD_ACTION', '["' .. KEYS[i] .. '","storm"]') + is_deadlock = true + time_left = detection_time + else + time_left = time_left - poll_time + end + else + if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then + redis.call('PUBLISH', 'PFC_WD_ACTION', '["' .. KEYS[i] .. '","restore"]') + end + time_left = detection_time + end + end + + -- Save values for next run + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) + if is_deadlock == false then + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) + redis.call('HDEL', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last', pfc_duration) + end + end + end + end +end + +return rets diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 3778ef1c5f5..df0977cb66f 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -4,6 +4,7 @@ #include "sai_serialize.h" #include "portsorch.h" #include +#include #define PFC_WD_QUEUE_STATUS "PFC_WD_STATUS" #define PFC_WD_QUEUE_STATUS_OPERATIONAL "operational" @@ -334,14 +335,14 @@ PfcWdLossyHandler::PfcWdLossyHandler(sai_object_id_t port, sai_object_id_t queue if (!gPortsOrch->getPortPfc(port, &pfcMask)) { - SWSS_LOG_ERROR("Failed to get PFC mask on port 0x%lx", port); + SWSS_LOG_ERROR("Failed to get PFC mask on port 0x%" PRIx64, port); } pfcMask = static_cast(pfcMask & ~(1 << queueId)); if (!gPortsOrch->setPortPfc(port, pfcMask)) { - SWSS_LOG_ERROR("Failed to set PFC mask on port 0x%lx", port); + SWSS_LOG_ERROR("Failed to set PFC mask on port 0x%" PRIx64, port); } } @@ -353,14 +354,14 @@ PfcWdLossyHandler::~PfcWdLossyHandler(void) if (!gPortsOrch->getPortPfc(getPort(), &pfcMask)) { - SWSS_LOG_ERROR("Failed to get PFC mask on port 0x%lx", getPort()); + SWSS_LOG_ERROR("Failed to get PFC mask on port 0x%" PRIx64, getPort()); } pfcMask = static_cast(pfcMask | (1 << getQueueId())); if (!gPortsOrch->setPortPfc(getPort(), pfcMask)) { - SWSS_LOG_ERROR("Failed to set PFC mask on port 0x%lx", getPort()); + SWSS_LOG_ERROR("Failed to set PFC mask on port 0x%" PRIx64, getPort()); } } @@ -391,7 +392,7 @@ bool PfcWdLossyHandler::getHwCounters(PfcWdHwStats& counters) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to fetch queue 0x%lx stats: %d", getQueue(), status); + SWSS_LOG_ERROR("Failed to fetch queue 0x%" PRIx64 " stats: %d", getQueue(), status); return false; } @@ -399,11 +400,11 @@ bool PfcWdLossyHandler::getHwCounters(PfcWdHwStats& counters) Port portInstance; if (!gPortsOrch->getPort(getPort(), portInstance)) { - SWSS_LOG_ERROR("Cannot get port by ID 0x%lx", getPort()); + SWSS_LOG_ERROR("Cannot get port by ID 0x%" PRIx64, getPort()); return false; } - sai_object_id_t pg = portInstance.m_priority_group_ids[getQueueId()]; + sai_object_id_t pg = portInstance.m_priority_group_ids[static_cast (getQueueId())]; vector pgStats; pgStats.resize(pgStatIds.size()); @@ -415,7 +416,7 @@ bool PfcWdLossyHandler::getHwCounters(PfcWdHwStats& counters) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to fetch pg 0x%lx stats: %d", pg, status); + SWSS_LOG_ERROR("Failed to fetch pg 0x%" PRIx64 " stats: %d", pg, status); return false; } @@ -440,7 +441,7 @@ PfcWdZeroBufferHandler::PfcWdZeroBufferHandler(sai_object_id_t port, sai_status_t status = sai_queue_api->get_queue_attribute(queue, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to get buffer profile ID on queue 0x%lx: %d", queue, status); + SWSS_LOG_ERROR("Failed to get buffer profile ID on queue 0x%" PRIx64 ": %d", queue, status); return; } @@ -453,7 +454,7 @@ PfcWdZeroBufferHandler::PfcWdZeroBufferHandler(sai_object_id_t port, status = sai_queue_api->set_queue_attribute(queue, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set buffer profile ID on queue 0x%lx: %d", queue, status); + SWSS_LOG_ERROR("Failed to set buffer profile ID on queue 0x%" PRIx64 ": %d", queue, status); return; } @@ -464,11 +465,11 @@ PfcWdZeroBufferHandler::PfcWdZeroBufferHandler(sai_object_id_t port, Port portInstance; if (!gPortsOrch->getPort(port, portInstance)) { - SWSS_LOG_ERROR("Cannot get port by ID 0x%lx", port); + SWSS_LOG_ERROR("Cannot get port by ID 0x%" PRIx64, port); return; } - sai_object_id_t pg = portInstance.m_priority_group_ids[queueId]; + sai_object_id_t pg = portInstance.m_priority_group_ids[static_cast (queueId)]; attr.id = SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE; @@ -476,7 +477,7 @@ PfcWdZeroBufferHandler::PfcWdZeroBufferHandler(sai_object_id_t port, status = sai_buffer_api->get_ingress_priority_group_attribute(pg, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to get buffer profile ID on PG 0x%lx: %d", pg, status); + SWSS_LOG_ERROR("Failed to get buffer profile ID on PG 0x%" PRIx64 ": %d", pg, status); return; } @@ -489,7 +490,7 @@ PfcWdZeroBufferHandler::PfcWdZeroBufferHandler(sai_object_id_t port, status = sai_buffer_api->set_ingress_priority_group_attribute(pg, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set buffer profile ID on pg 0x%lx: %d", pg, status); + SWSS_LOG_ERROR("Failed to set buffer profile ID on pg 0x%" PRIx64 ": %d", pg, status); return; } @@ -509,18 +510,18 @@ PfcWdZeroBufferHandler::~PfcWdZeroBufferHandler(void) sai_status_t status = sai_queue_api->set_queue_attribute(getQueue(), &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set buffer profile ID on queue 0x%lx: %d", getQueue(), status); + SWSS_LOG_ERROR("Failed to set buffer profile ID on queue 0x%" PRIx64 ": %d", getQueue(), status); return; } Port portInstance; if (!gPortsOrch->getPort(getPort(), portInstance)) { - SWSS_LOG_ERROR("Cannot get port by ID 0x%lx", getPort()); + SWSS_LOG_ERROR("Cannot get port by ID 0x%" PRIx64, getPort()); return; } - sai_object_id_t pg = portInstance.m_priority_group_ids[getQueueId()]; + sai_object_id_t pg = portInstance.m_priority_group_ids[size_t(getQueueId())]; attr.id = SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE; attr.value.oid = m_originalPgBufferProfile; @@ -529,7 +530,7 @@ PfcWdZeroBufferHandler::~PfcWdZeroBufferHandler(void) status = sai_buffer_api->set_ingress_priority_group_attribute(pg, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set buffer profile ID on queue 0x%lx: %d", getQueue(), status); + SWSS_LOG_ERROR("Failed to set buffer profile ID on queue 0x%" PRIx64 ": %d", getQueue(), status); return; } } diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index 043ea64a03f..c4002a1c3d1 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "pfcwdorch.h" #include "sai_serialize.h" @@ -53,7 +54,7 @@ void PfcWdOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -352,7 +353,7 @@ void PfcWdSwOrch::disableBigRedSwitchMode() if (entry.second.handler != nullptr) { SWSS_LOG_NOTICE( - "PFC Watchdog BIG_RED_SWITCH mode disabled on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + "PFC Watchdog BIG_RED_SWITCH mode disabled on port %s, queue index %d, queue id 0x%" PRIx64 " and port id 0x%" PRIx64 ".", entry.second.portAlias.c_str(), entry.second.index, entry.first, @@ -456,7 +457,7 @@ void PfcWdSwOrch::enableBigRedSwitchMode() if (entry->second.handler== nullptr) { SWSS_LOG_NOTICE( - "PFC Watchdog BIG_RED_SWITCH mode enabled on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + "PFC Watchdog BIG_RED_SWITCH mode enabled on port %s, queue index %d, queue id 0x%" PRIx64 " and port id 0x%" PRIx64 ".", entry->second.portAlias.c_str(), entry->second.index, entry->first, @@ -900,7 +901,7 @@ bool PfcWdSwOrch::startWdActionOnQueue(const string auto entry = m_entryMap.find(queueId); if (entry == m_entryMap.end()) { - SWSS_LOG_ERROR("Queue 0x%lx is not registered", queueId); + SWSS_LOG_ERROR("Queue 0x%" PRIx64 " is not registered", queueId); return false; } @@ -917,7 +918,7 @@ bool PfcWdSwOrch::startWdActionOnQueue(const string if (entry->second.handler == nullptr) { SWSS_LOG_NOTICE( - "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%" PRIx64 " and port id 0x%" PRIx64 ".", entry->second.portAlias.c_str(), entry->second.index, entry->first, @@ -939,7 +940,7 @@ bool PfcWdSwOrch::startWdActionOnQueue(const string if (entry->second.handler == nullptr) { SWSS_LOG_NOTICE( - "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%" PRIx64 " and port id 0x%" PRIx64 ".", entry->second.portAlias.c_str(), entry->second.index, entry->first, @@ -961,7 +962,7 @@ bool PfcWdSwOrch::startWdActionOnQueue(const string if (entry->second.handler == nullptr) { SWSS_LOG_NOTICE( - "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%" PRIx64 " and port id 0x%" PRIx64 ".", entry->second.portAlias.c_str(), entry->second.index, entry->first, @@ -989,7 +990,7 @@ bool PfcWdSwOrch::startWdActionOnQueue(const string if (entry->second.handler != nullptr) { SWSS_LOG_NOTICE( - "PFC Watchdog storm restored on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + "PFC Watchdog storm restored on port %s, queue index %d, queue id 0x%" PRIx64 " and port id 0x%" PRIx64 ".", entry->second.portAlias.c_str(), entry->second.index, entry->first, diff --git a/orchagent/policerorch.cpp b/orchagent/policerorch.cpp index 3aecf822c96..3c102487c87 100644 --- a/orchagent/policerorch.cpp +++ b/orchagent/policerorch.cpp @@ -2,6 +2,7 @@ #include "policerorch.h" #include "converter.h" +#include using namespace std; using namespace swss; @@ -63,7 +64,7 @@ bool PolicerOrch::getPolicerOid(const string &name, sai_object_id_t &oid) if (policerExists(name)) { oid = m_syncdPolicers[name]; - SWSS_LOG_NOTICE("Get policer %s oid:%lx", name.c_str(), oid); + SWSS_LOG_NOTICE("Get policer %s oid:%" PRIx64, name.c_str(), oid); return true; } @@ -114,7 +115,7 @@ void PolicerOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -129,12 +130,8 @@ void PolicerOrch::doTask(Consumer &consumer) if (op == SET_COMMAND) { - if (m_syncdPolicers.find(key) != m_syncdPolicers.end()) - { - SWSS_LOG_ERROR("Policer %s already exists", key.c_str()); - it = consumer.m_toSync.erase(it); - continue; - } + // Mark the opeartion as an 'update', if the policer exists. + bool update = m_syncdPolicers.find(key) != m_syncdPolicers.end(); vector attrs; bool meter_type = false, mode = false; @@ -211,26 +208,64 @@ void PolicerOrch::doTask(Consumer &consumer) attrs.push_back(attr); } - if (!meter_type || !mode) + // Create a new policer + if (!update) { - SWSS_LOG_ERROR("Failed to create policer %s,\ - missing madatory fields", key.c_str()); - } + if (!meter_type || !mode) + { + SWSS_LOG_ERROR("Failed to create policer %s,\ + missing mandatory fields", key.c_str()); + } - sai_object_id_t policer_id; - sai_status_t status = sai_policer_api->create_policer( - &policer_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); - if (status != SAI_STATUS_SUCCESS) + sai_object_id_t policer_id; + sai_status_t status = sai_policer_api->create_policer( + &policer_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create policer %s, rv:%d", + key.c_str(), status); + it++; + continue; + } + + SWSS_LOG_NOTICE("Created policer %s", key.c_str()); + m_syncdPolicers[key] = policer_id; + m_policerRefCounts[key] = 0; + } + // Update an existing policer + else { - SWSS_LOG_ERROR("Failed to create policer %s, rv:%d", - key.c_str(), status); - it++; - continue; + auto policer_id = m_syncdPolicers[key]; + + // The update operation has limitations that it could only update + // the rate and the size accordingly. + // SR_TCM: CIR, CBS, PBS + // TR_TCM: CIR, CBS, PIR, PBS + // STORM_CONTROL: CIR, CBS + for (auto & attr: attrs) + { + if (attr.id != SAI_POLICER_ATTR_CBS && + attr.id != SAI_POLICER_ATTR_CIR && + attr.id != SAI_POLICER_ATTR_PBS && + attr.id != SAI_POLICER_ATTR_PIR) + { + continue; + } + + sai_status_t status = sai_policer_api->set_policer_attribute( + policer_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update policer %s attribute, rv:%d", + key.c_str(), status); + it++; + continue; + } + } + + SWSS_LOG_NOTICE("Update policer %s attributes", key.c_str()); } - SWSS_LOG_NOTICE("Created policer %s", key.c_str()); - m_syncdPolicers[key] = policer_id; - m_policerRefCounts[key] = 0; it = consumer.m_toSync.erase(it); } else if (op == DEL_COMMAND) diff --git a/orchagent/port.h b/orchagent/port.h index 34b72aa8c2d..ad264a1db56 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -30,8 +30,8 @@ typedef std::map vlan_members_t; struct VlanInfo { - sai_object_id_t vlan_oid; - sai_vlan_id_t vlan_id; + sai_object_id_t vlan_oid = 0; + sai_vlan_id_t vlan_id = 0; }; class Port diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 5b1cdae88ac..69015937548 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -3,6 +3,7 @@ #include "bufferorch.h" #include "neighorch.h" +#include #include #include #include @@ -198,9 +199,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ_AND_CLEAR); m_flexCounterGroupTable->set(PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues); } - catch (...) + catch (const runtime_error &e) { - SWSS_LOG_WARN("Watermark flex counter groups were not set successfully"); + SWSS_LOG_ERROR("Watermark flex counter groups were not set successfully: %s", e.what()); } uint32_t i, j; @@ -261,7 +262,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) 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:%lx", port_list[i]); + SWSS_LOG_ERROR("Failed to get hardware lane list pid:%" PRIx64, port_list[i]); throw runtime_error("PortsOrch initialization failure"); } @@ -278,7 +279,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) } tmp_lane_str = tmp_lane_str.substr(0, tmp_lane_str.size()-1); - SWSS_LOG_NOTICE("Get port with lanes pid:%lx lanes:%s", port_list[i], tmp_lane_str.c_str()); + 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]; } @@ -388,7 +389,7 @@ void PortsOrch::removeDefaultBridgePorts() SWSS_LOG_NOTICE("Remove bridge ports from default 1Q bridge"); } -bool PortsOrch::isPortReady() +bool PortsOrch::allPortsReady() { return m_initDone && m_pendingPortSet.empty(); } @@ -546,11 +547,11 @@ bool PortsOrch::setPortAdminStatus(sai_object_id_t id, bool up) sai_status_t status = sai_port_api->set_port_attribute(id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set admin status %s to port pid:%lx", + SWSS_LOG_ERROR("Failed to set admin status %s to port pid:%" PRIx64, up ? "UP" : "DOWN", id); return false; } - SWSS_LOG_INFO("Set admin status %s to port pid:%lx", + SWSS_LOG_INFO("Set admin status %s to port pid:%" PRIx64, up ? "UP" : "DOWN", id); return true; } @@ -565,7 +566,7 @@ bool PortsOrch::getPortAdminStatus(sai_object_id_t id, bool &up) sai_status_t status = sai_port_api->get_port_attribute(id, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to get admin status for port pid:%lx", id); + SWSS_LOG_ERROR("Failed to get admin status for port pid:%" PRIx64, id); return false; } @@ -586,11 +587,11 @@ bool PortsOrch::setPortMtu(sai_object_id_t id, sai_uint32_t mtu) sai_status_t status = sai_port_api->set_port_attribute(id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set MTU %u to port pid:%lx, rv:%d", + SWSS_LOG_ERROR("Failed to set MTU %u to port pid:%" PRIx64 ", rv:%d", attr.value.u32, id, status); return false; } - SWSS_LOG_INFO("Set MTU %u to port pid:%lx", attr.value.u32, id); + SWSS_LOG_INFO("Set MTU %u to port pid:%" PRIx64, attr.value.u32, id); return true; } @@ -605,11 +606,11 @@ bool PortsOrch::setPortFec(sai_object_id_t id, sai_port_fec_mode_t mode) sai_status_t status = sai_port_api->set_port_attribute(id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set fec mode %d to port pid:%lx", + SWSS_LOG_ERROR("Failed to set fec mode %d to port pid:%" PRIx64, mode, id); return false; } - SWSS_LOG_INFO("Set fec mode %d to port pid:%lx", + SWSS_LOG_INFO("Set fec mode %d to port pid:%" PRIx64, mode, id); return true; } @@ -622,7 +623,7 @@ bool PortsOrch::getPortPfc(sai_object_id_t portId, uint8_t *pfc_bitmask) if (!getPort(portId, p)) { - SWSS_LOG_ERROR("Failed to get port object for port id 0x%lx", portId); + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, portId); return false; } @@ -640,7 +641,7 @@ bool PortsOrch::setPortPfc(sai_object_id_t portId, uint8_t pfc_bitmask) if (!getPort(portId, p)) { - SWSS_LOG_ERROR("Failed to get port object for port id 0x%lx", portId); + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, portId); return false; } @@ -663,7 +664,7 @@ bool PortsOrch::setPortPfc(sai_object_id_t portId, uint8_t pfc_bitmask) sai_status_t status = sai_port_api->set_port_attribute(portId, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set PFC 0x%x to port id 0x%lx (rc:%d)", attr.value.u8, portId, status); + SWSS_LOG_ERROR("Failed to set PFC 0x%x to port id 0x%" PRIx64 " (rc:%d)", attr.value.u8, portId, status); return false; } @@ -711,7 +712,7 @@ bool PortsOrch::setPortPfcAsym(Port &port, string 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%lx (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)", port.m_pfc_asym, port.m_port_id, status); return false; } @@ -728,12 +729,12 @@ bool PortsOrch::setPortPfcAsym(Port &port, string 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 RX PFC 0x%x to port id 0x%lx (rc:%d)", attr.value.u8, port.m_port_id, status); + 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); return false; } } - SWSS_LOG_INFO("Set asymmetric PFC %s to port id 0x%lx", pfc_asym.c_str(), port.m_port_id); + SWSS_LOG_INFO("Set asymmetric PFC %s to port id 0x%" PRIx64, pfc_asym.c_str(), port.m_port_id); return true; } @@ -751,7 +752,7 @@ bool PortsOrch::createBindAclTableGroup(sai_object_id_t id, sai_object_id_t &gro Port port; if (!getPort(id, port)) { - SWSS_LOG_ERROR("Failed to get port by port ID %lx", id); + SWSS_LOG_ERROR("Failed to get port by port ID %" PRIx64, id); return false; } @@ -837,7 +838,7 @@ bool PortsOrch::createBindAclTableGroup(sai_object_id_t id, sai_object_id_t &gro status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to bind port %s to ACL table group %lx, rv:%d", + SWSS_LOG_ERROR("Failed to bind port %s to ACL table group %" PRIx64 ", rv:%d", port.m_alias.c_str(), group_oid, status); return false; } @@ -853,7 +854,7 @@ bool PortsOrch::createBindAclTableGroup(sai_object_id_t id, sai_object_id_t &gro status = sai_lag_api->set_lag_attribute(port.m_lag_id, &lag_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to bind LAG %s to ACL table group %lx, rv:%d", + SWSS_LOG_ERROR("Failed to bind LAG %s to ACL table group %" PRIx64 ", rv:%d", port.m_alias.c_str(), group_oid, status); return false; } @@ -869,7 +870,7 @@ bool PortsOrch::createBindAclTableGroup(sai_object_id_t id, sai_object_id_t &gro status = sai_vlan_api->set_vlan_attribute(port.m_vlan_info.vlan_oid, &vlan_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to bind VLAN %s to ACL table group %lx, rv:%d", + SWSS_LOG_ERROR("Failed to bind VLAN %s to ACL table group %" PRIx64 ", rv:%d", port.m_alias.c_str(), group_oid, status); return false; } @@ -894,7 +895,7 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ if (table_oid == SAI_NULL_OBJECT_ID) { - SWSS_LOG_ERROR("Invalid ACL table %lx", table_oid); + SWSS_LOG_ERROR("Invalid ACL table %" PRIx64, table_oid); return false; } @@ -904,7 +905,7 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ // Create an ACL table group and bind to port if (!createBindAclTableGroup(id, groupOid, acl_stage)) { - SWSS_LOG_ERROR("Fail to create or bind to port %lx ACL table group", id); + SWSS_LOG_ERROR("Fail to create or bind to port %" PRIx64 " ACL table group", id); return false; } @@ -927,7 +928,7 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ status = sai_acl_api->create_acl_table_group_member(&group_member_oid, gSwitchId, (uint32_t)member_attrs.size(), member_attrs.data()); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to create member in ACL table group %lx for ACL table %lx, rv:%d", + SWSS_LOG_ERROR("Failed to create member in ACL table group %" PRIx64 " for ACL table %" PRIx64 ", rv:%d", groupOid, table_oid, status); return false; } @@ -1086,7 +1087,7 @@ bool PortsOrch::isSpeedSupported(const std::string& alias, sai_object_id_t port_ if (status == SAI_STATUS_BUFFER_OVERFLOW) { // something went wrong in SAI implementation - SWSS_LOG_ERROR("Failed to get supported speed list for port %s id=%lx. Not enough container size", + SWSS_LOG_ERROR("Failed to get supported speed list for port %s id=%" PRIx64 ". Not enough container size", alias.c_str(), port_id); } else if (SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || @@ -1095,12 +1096,12 @@ bool PortsOrch::isSpeedSupported(const std::string& alias, sai_object_id_t port_ { // unable to validate speed if attribute is not supported on platform // assuming input value is correct - SWSS_LOG_WARN("Unable to validate speed for port %s id=%lx. Not supported by platform", + SWSS_LOG_WARN("Unable to validate speed for port %s id=%" PRIx64 ". Not supported by platform", alias.c_str(), port_id); } else { - SWSS_LOG_ERROR("Failed to get a list of supported speeds for port %s id=%lx. Error=%d", + SWSS_LOG_ERROR("Failed to get a list of supported speeds for port %s id=%" PRIx64 ". Error=%d", alias.c_str(), port_id, status); } m_portSupportedSpeeds[port_id] = {}; // use an empty list, @@ -1180,7 +1181,7 @@ bool PortsOrch::getQueueTypeAndIndex(sai_object_id_t queue_id, string &type, uin sai_status_t status = sai_queue_api->get_queue_attribute(queue_id, 2, attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to get queue type and index for queue %lu rv:%d", queue_id, status); + SWSS_LOG_ERROR("Failed to get queue type and index for queue %" PRIu64 " rv:%d", queue_id, status); return false; } @@ -1196,7 +1197,7 @@ bool PortsOrch::getQueueTypeAndIndex(sai_object_id_t queue_id, string &type, uin type = "SAI_QUEUE_TYPE_MULTICAST"; break; default: - SWSS_LOG_ERROR("Got unsupported queue type %d for %lu queue", attr[0].value.s32, queue_id); + SWSS_LOG_ERROR("Got unsupported queue type %d for %" PRIu64 " queue", attr[0].value.s32, queue_id); throw runtime_error("Got unsupported queue type"); } @@ -1223,10 +1224,10 @@ bool PortsOrch::setPortAutoNeg(sai_object_id_t id, int an) sai_status_t status = sai_port_api->set_port_attribute(id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set AutoNeg %u to port pid:%lx", attr.value.booldata, id); + SWSS_LOG_ERROR("Failed to set AutoNeg %u to port pid:%" PRIx64, attr.value.booldata, id); return false; } - SWSS_LOG_INFO("Set AutoNeg %u to port pid:%lx", attr.value.booldata, id); + SWSS_LOG_INFO("Set AutoNeg %u to port pid:%" PRIx64, attr.value.booldata, id); return true; } @@ -1304,7 +1305,7 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string m_portListLaneMap[lane_set] = port_id; - SWSS_LOG_NOTICE("Create port %lx with the speed %u", port_id, speed); + SWSS_LOG_NOTICE("Create port %" PRIx64 " with the speed %u", port_id, speed); return true; } @@ -1323,11 +1324,11 @@ bool PortsOrch::removePort(sai_object_id_t port_id) sai_status_t status = sai_port_api->remove_port(port_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove port %lx, rv:%d", port_id, status); + SWSS_LOG_ERROR("Failed to remove port %" PRIx64 ", rv:%d", port_id, status); return false; } removeAclTableGroup(p); - SWSS_LOG_NOTICE("Remove port %lx", port_id); + SWSS_LOG_NOTICE("Remove port %" PRIx64, port_id); return true; } @@ -1429,7 +1430,8 @@ bool PortsOrch::bake() vector tuples; string value; bool foundPortConfigDone = m_portTable->hget("PortConfigDone", "count", value); - unsigned long portCount; + uintmax_t portCount; + char* endPtr = NULL; SWSS_LOG_NOTICE("foundPortConfigDone = %d", foundPortConfigDone); bool foundPortInitDone = m_portTable->get("PortInitDone", tuples); @@ -1446,12 +1448,12 @@ bool PortsOrch::bake() return false; } - portCount = stoul(value); - SWSS_LOG_NOTICE("portCount = %lu, m_portCount = %u", portCount, m_portCount); + portCount = strtoumax(value.c_str(), &endPtr, 0); + SWSS_LOG_NOTICE("portCount = %" PRIuMAX ", m_portCount = %u", portCount, m_portCount); if (portCount != keys.size() - 2) { // Invalid port table - SWSS_LOG_ERROR("Invalid port table: portCount, expecting %lu, got %lu", + SWSS_LOG_ERROR("Invalid port table: portCount, expecting %" PRIuMAX ", got %zu", portCount, keys.size() - 2); cleanPortTable(keys); @@ -1635,19 +1637,10 @@ void PortsOrch::doPortTask(Consumer &consumer) { if (m_lanesAliasSpeedMap.find(it->first) == m_lanesAliasSpeedMap.end()) { - char *platform = getenv("platform"); - if (platform && (strstr(platform, BFN_PLATFORM_SUBSTRING) || strstr(platform, MLNX_PLATFORM_SUBSTRING))) + if (!removePort(it->second)) { - if (!removePort(it->second)) - { - throw runtime_error("PortsOrch initialization failure."); - } - } - else - { - SWSS_LOG_NOTICE("Failed to remove Port %lx due to missing SAI remove_port API.", it->second); + throw runtime_error("PortsOrch initialization failure."); } - it = m_portListLaneMap.erase(it); } else @@ -1662,22 +1655,11 @@ void PortsOrch::doPortTask(Consumer &consumer) if (m_portListLaneMap.find(it->first) == m_portListLaneMap.end()) { - // work around to avoid syncd termination on SAI error due missing create_port SAI API - // can be removed when SAI redis return NotImplemented error - char *platform = getenv("platform"); - if (platform && (strstr(platform, BFN_PLATFORM_SUBSTRING) || strstr(platform, MLNX_PLATFORM_SUBSTRING))) - { - if (!addPort(it->first, get<1>(it->second), get<2>(it->second), get<3>(it->second))) - { - throw runtime_error("PortsOrch initialization failure."); - } - - port_created = true; - } - else + if (!addPort(it->first, get<1>(it->second), get<2>(it->second), get<3>(it->second))) { - SWSS_LOG_NOTICE("Failed to create Port %s due to missing SAI create_port API.", get<0>(it->second).c_str()); + throw runtime_error("PortsOrch initialization failure."); } + port_created = true; } else { @@ -2192,6 +2174,17 @@ void PortsOrch::doLagTask(Consumer &consumer) { mtu = (uint32_t)stoul(fvValue(i)); } + if (fvField(i) == "oper_status") + { + if (fvValue(i) == "down") + { + gNeighOrch->ifChangeInformNextHop(alias, false); + } + else + { + gNeighOrch->ifChangeInformNextHop(alias, true); + } + } } // Create a new LAG when the new alias comes @@ -2339,7 +2332,7 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) if (!port.m_lag_id || !port.m_lag_member_id) { - SWSS_LOG_WARN("Member %s not found in LAG %s lid:%lx lmid:%lx,", + SWSS_LOG_WARN("Member %s not found in LAG %s lid:%" PRIx64 " lmid:%" PRIx64 ",", port.m_alias.c_str(), lag.m_alias.c_str(), lag.m_lag_id, port.m_lag_member_id); it = consumer.m_toSync.erase(it); continue; @@ -2371,7 +2364,7 @@ void PortsOrch::doTask(Consumer &consumer) else { /* Wait for all ports to be initialized */ - if (!isPortReady()) + if (!allPortsReady()) { return; } @@ -2468,7 +2461,7 @@ bool PortsOrch::initializePort(Port &port) { SWSS_LOG_ENTER(); - SWSS_LOG_NOTICE("Initializing port alias:%s pid:%lx", port.m_alias.c_str(), port.m_port_id); + SWSS_LOG_NOTICE("Initializing port alias:%s pid:%" PRIx64, port.m_alias.c_str(), port.m_port_id); initializePriorityGroups(port); initializeQueues(port); @@ -2588,10 +2581,10 @@ bool PortsOrch::setBridgePortLearningFDB(Port &port, sai_bridge_port_fdb_learnin auto status = sai_bridge_api->set_bridge_port_attribute(bridge_port_id, &bport_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set bridge port %lx learning_mode attribute: %d", bridge_port_id, status); + SWSS_LOG_ERROR("Failed to set bridge port %" PRIx64 " learning_mode attribute: %d", bridge_port_id, status); return false; } - SWSS_LOG_NOTICE("Disable FDB learning on bridge port %s(%lx)", port.m_alias.c_str(), bridge_port_id); + SWSS_LOG_NOTICE("Disable FDB learning on bridge port %s(%" PRIx64 ")", port.m_alias.c_str(), bridge_port_id); return true; } @@ -2741,7 +2734,7 @@ bool PortsOrch::removeVlan(Port vlan) SWSS_LOG_ENTER(); if (m_port_ref_count[vlan.m_alias] > 0) { - SWSS_LOG_ERROR("Failed to remove ref count %d VLAN %s", + SWSS_LOG_ERROR("Failed to remove ref count %d VLAN %s", m_port_ref_count[vlan.m_alias], vlan.m_alias.c_str()); return false; @@ -2820,11 +2813,11 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) sai_status_t status = sai_vlan_api->create_vlan_member(&vlan_member_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to add member %s to VLAN %s vid:%hu pid:%lx", + SWSS_LOG_ERROR("Failed to add member %s to VLAN %s vid:%hu pid:%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, port.m_port_id); return false; } - SWSS_LOG_NOTICE("Add member %s to VLAN %s vid:%hu pid%lx", + SWSS_LOG_NOTICE("Add member %s to VLAN %s vid:%hu pid%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, port.m_port_id); /* Use untagged VLAN as pvid of the member port */ @@ -2865,12 +2858,12 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) sai_status_t status = sai_vlan_api->remove_vlan_member(vlan_member_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove member %s from VLAN %s vid:%hx vmid:%lx", + SWSS_LOG_ERROR("Failed to remove member %s from VLAN %s vid:%hx vmid:%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, vlan_member_id); return false; } port.m_vlan_members.erase(vlan_member); - SWSS_LOG_NOTICE("Remove member %s from VLAN %s lid:%hx vmid:%lx", + SWSS_LOG_NOTICE("Remove member %s from VLAN %s lid:%hx vmid:%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, vlan_member_id); /* Restore to default pvid if this port joined this VLAN in untagged mode previously */ @@ -2901,11 +2894,11 @@ bool PortsOrch::addLag(string lag_alias) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to create LAG %s lid:%lx", lag_alias.c_str(), lag_id); + SWSS_LOG_ERROR("Failed to create LAG %s lid:%" PRIx64, lag_alias.c_str(), lag_id); return false; } - SWSS_LOG_NOTICE("Create an empty LAG %s lid:%lx", lag_alias.c_str(), lag_id); + SWSS_LOG_NOTICE("Create an empty LAG %s lid:%" PRIx64, lag_alias.c_str(), lag_id); Port lag(lag_alias, Port::LAG); lag.m_lag_id = lag_id; @@ -2925,8 +2918,8 @@ bool PortsOrch::removeLag(Port lag) if (m_port_ref_count[lag.m_alias] > 0) { - SWSS_LOG_ERROR("Failed to remove ref count %d LAG %s", - m_port_ref_count[lag.m_alias], + SWSS_LOG_ERROR("Failed to remove ref count %d LAG %s", + m_port_ref_count[lag.m_alias], lag.m_alias.c_str()); return false; } @@ -2946,13 +2939,13 @@ bool PortsOrch::removeLag(Port lag) sai_status_t status = sai_lag_api->remove_lag(lag.m_lag_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove LAG %s lid:%lx", lag.m_alias.c_str(), lag.m_lag_id); + SWSS_LOG_ERROR("Failed to remove LAG %s lid:%" PRIx64, lag.m_alias.c_str(), lag.m_lag_id); return false; } removeAclTableGroup(lag); - SWSS_LOG_NOTICE("Remove LAG %s lid:%lx", lag.m_alias.c_str(), lag.m_lag_id); + SWSS_LOG_NOTICE("Remove LAG %s lid:%" PRIx64, lag.m_alias.c_str(), lag.m_lag_id); m_portList.erase(lag.m_alias); m_port_ref_count.erase(lag.m_alias); @@ -3004,12 +2997,12 @@ bool PortsOrch::addLagMember(Port &lag, Port &port) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to add member %s to LAG %s lid:%lx pid:%lx", + SWSS_LOG_ERROR("Failed to add member %s to LAG %s lid:%" PRIx64 " pid:%" PRIx64, port.m_alias.c_str(), lag.m_alias.c_str(), lag.m_lag_id, port.m_port_id); return false; } - SWSS_LOG_NOTICE("Add member %s to LAG %s lid:%lx pid:%lx", + SWSS_LOG_NOTICE("Add member %s to LAG %s lid:%" PRIx64 " pid:%" PRIx64, port.m_alias.c_str(), lag.m_alias.c_str(), lag.m_lag_id, port.m_port_id); port.m_lag_id = lag.m_lag_id; @@ -3041,12 +3034,12 @@ bool PortsOrch::removeLagMember(Port &lag, Port &port) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove member %s from LAG %s lid:%lx lmid:%lx", + SWSS_LOG_ERROR("Failed to remove member %s from LAG %s lid:%" PRIx64 " lmid:%" PRIx64, port.m_alias.c_str(), lag.m_alias.c_str(), lag.m_lag_id, port.m_lag_member_id); return false; } - SWSS_LOG_NOTICE("Remove member %s from LAG %s lid:%lx lmid:%lx", + SWSS_LOG_NOTICE("Remove member %s from LAG %s lid:%" PRIx64 " lmid:%" PRIx64, port.m_alias.c_str(), lag.m_alias.c_str(), lag.m_lag_id, port.m_lag_member_id); port.m_lag_id = 0; @@ -3222,7 +3215,7 @@ void PortsOrch::doTask(NotificationConsumer &consumer) SWSS_LOG_ENTER(); /* Wait for all ports to be initialized */ - if (!isPortReady()) + if (!allPortsReady()) { return; } @@ -3250,13 +3243,13 @@ void PortsOrch::doTask(NotificationConsumer &consumer) sai_object_id_t id = portoperstatus[i].port_id; sai_port_oper_status_t status = portoperstatus[i].port_state; - SWSS_LOG_NOTICE("Get port state change notification id:%lx status:%d", id, status); + SWSS_LOG_NOTICE("Get port state change notification id:%" PRIx64 " status:%d", id, status); Port port; if (!getPort(id, port)) { - SWSS_LOG_ERROR("Failed to get port object for port id 0x%lx", id); + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, id); continue; } @@ -3412,7 +3405,7 @@ bool PortsOrch::setPortSerdesAttribute(sai_object_id_t port_id, sai_attr_id_t at sai_status_t status = sai_port_api->set_port_attribute(port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set serdes attribute %d to port pid:%lx", + SWSS_LOG_ERROR("Failed to set serdes attribute %d to port pid:%" PRIx64, attr_id, port_id); return false; } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index a579e7f545b..d2819e62eaf 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -55,7 +55,7 @@ class PortsOrch : public Orch, public Subject public: PortsOrch(DBConnector *db, vector &tableNames); - bool isPortReady(); + bool allPortsReady(); bool isInitDone(); map& getAllPorts(); diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 8859f8ef4bd..4e92275d79f 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -2,7 +2,9 @@ #include "qosorch.h" #include "logger.h" #include "crmorch.h" +#include "sai_serialize.h" +#include #include #include #include @@ -40,8 +42,10 @@ enum { RED_DROP_PROBABILITY_SET = (1U << 2) }; +// field_name is what is expected in CONFIG_DB PORT_QOS_MAP table map qos_to_attr_map = { {dscp_to_tc_field_name, SAI_PORT_ATTR_QOS_DSCP_TO_TC_MAP}, + {dot1p_to_tc_field_name, SAI_PORT_ATTR_QOS_DOT1P_TO_TC_MAP}, {tc_to_queue_field_name, SAI_PORT_ATTR_QOS_TC_TO_QUEUE_MAP}, {tc_to_pg_map_field_name, SAI_PORT_ATTR_QOS_TC_TO_PRIORITY_GROUP_MAP}, {pfc_to_pg_map_name, SAI_PORT_ATTR_QOS_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP}, @@ -50,6 +54,7 @@ map qos_to_attr_map = { type_map QosOrch::m_qos_maps = { {CFG_DSCP_TO_TC_MAP_TABLE_NAME, new object_map()}, + {CFG_DOT1P_TO_TC_MAP_TABLE_NAME, new object_map()}, {CFG_TC_TO_QUEUE_MAP_TABLE_NAME, new object_map()}, {CFG_SCHEDULER_TABLE_NAME, new object_map()}, {CFG_WRED_PROFILE_TABLE_NAME, new object_map()}, @@ -115,7 +120,7 @@ task_process_status QosMapHandler::processWorkItem(Consumer& consumer) } if (!removeQosItem(sai_object)) { - SWSS_LOG_ERROR("Failed to remove dscp_to_tc map. db name:%s sai object:%lx", qos_object_name.c_str(), sai_object); + SWSS_LOG_ERROR("Failed to remove dscp_to_tc map. db name:%s sai object:%" PRIx64, qos_object_name.c_str(), sai_object); return task_process_status::task_failed; } auto it_to_delete = (QosOrch::getTypeMap()[qos_map_type_name])->find(qos_object_name); @@ -144,7 +149,7 @@ bool QosMapHandler::modifyQosItem(sai_object_id_t sai_object, vectorremove_qos_map(sai_object); if (SAI_STATUS_SUCCESS != sai_status) { @@ -204,7 +209,7 @@ sai_object_id_t DscpToTcMapHandler::addQosItem(const vector &at SWSS_LOG_ERROR("Failed to create dscp_to_tc map. status:%d", sai_status); return SAI_NULL_OBJECT_ID; } - SWSS_LOG_DEBUG("created QosMap object:%lx", sai_object); + SWSS_LOG_DEBUG("created QosMap object:%" PRIx64, sai_object); return sai_object; } @@ -215,6 +220,75 @@ task_process_status QosOrch::handleDscpToTcTable(Consumer& consumer) return dscp_tc_handler.processWorkItem(consumer); } +bool Dot1pToTcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_qos_map_list_t dot1p_map_list; + + // Allocated resources are freed in freeAttribResources() call + dot1p_map_list.list = new sai_qos_map_t[kfvFieldsValues(tuple).size()]; + int i = 0; + for (const auto &fv : kfvFieldsValues(tuple)) + { + try + { + dot1p_map_list.list[i].key.dot1p = static_cast(stoi(fvField(fv))); + dot1p_map_list.list[i].value.tc = static_cast(stoi(fvValue(fv))); + } + catch (const std::invalid_argument &e) + { + SWSS_LOG_ERROR("Invalid dot1p to tc argument %s:%s to %s()", fvField(fv).c_str(), fvValue(fv).c_str(), e.what()); + continue; + } + catch (const std::out_of_range &e) + { + SWSS_LOG_ERROR("Out of range dot1p to tc argument %s:%s to %s()", fvField(fv).c_str(), fvValue(fv).c_str(), e.what()); + continue; + } + + i++; + } + dot1p_map_list.count = static_cast(i); + + sai_attribute_t attr; + attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + attr.value.qosmap.count = dot1p_map_list.count; + attr.value.qosmap.list = dot1p_map_list.list; + attributes.push_back(attr); + + return true; +} + +sai_object_id_t Dot1pToTcMapHandler::addQosItem(const vector &attributes) +{ + SWSS_LOG_ENTER(); + vector attrs; + + sai_attribute_t attr; + attr.id = SAI_QOS_MAP_ATTR_TYPE; + attr.value.u32 = SAI_QOS_MAP_TYPE_DOT1P_TO_TC; + attrs.push_back(attr); + + attrs.push_back(attributes[0]); + + sai_object_id_t object_id; + sai_status_t sai_status = sai_qos_map_api->create_qos_map(&object_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create dot1p_to_tc map. status: %s", sai_serialize_status(sai_status).c_str()); + return SAI_NULL_OBJECT_ID; + } + SWSS_LOG_DEBUG("created QosMap object: 0x%lx", object_id); + return object_id; +} + +task_process_status QosOrch::handleDot1pToTcTable(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + Dot1pToTcMapHandler dot1p_tc_handler; + return dot1p_tc_handler.processWorkItem(consumer); +} + bool TcToQueueMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) { SWSS_LOG_ENTER(); @@ -799,6 +873,7 @@ void QosOrch::initTableHandlers() { SWSS_LOG_ENTER(); m_qos_handler_map.insert(qos_handler_pair(CFG_DSCP_TO_TC_MAP_TABLE_NAME, &QosOrch::handleDscpToTcTable)); + m_qos_handler_map.insert(qos_handler_pair(CFG_DOT1P_TO_TC_MAP_TABLE_NAME, &QosOrch::handleDot1pToTcTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_TC_TO_QUEUE_MAP_TABLE_NAME, &QosOrch::handleTcToQueueTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_SCHEDULER_TABLE_NAME, &QosOrch::handleSchedulerTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_QUEUE_TABLE_NAME, &QosOrch::handleQueueTable)); @@ -869,6 +944,30 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) // TODO: The meaning is to be able to adjus priority of the given scheduler group. // However currently SAI model does not provide such ability. } + else if (fvField(*i) == scheduler_min_bandwidth_rate_field_name) + { + attr.id = SAI_SCHEDULER_ATTR_MIN_BANDWIDTH_RATE; + attr.value.u64 = (uint64_t)stoi(fvValue(*i)); + sai_attr_list.push_back(attr); + } + else if (fvField(*i) == scheduler_min_bandwidth_burst_rate_field_name) + { + attr.id = SAI_SCHEDULER_ATTR_MIN_BANDWIDTH_BURST_RATE; + attr.value.u64 = (uint64_t)stoi(fvValue(*i)); + sai_attr_list.push_back(attr); + } + else if (fvField(*i) == scheduler_max_bandwidth_rate_field_name) + { + attr.id = SAI_SCHEDULER_ATTR_MAX_BANDWIDTH_RATE; + attr.value.u64 = (uint64_t)stoi(fvValue(*i)); + sai_attr_list.push_back(attr); + } + else if (fvField(*i) == scheduler_max_bandwidth_burst_rate_field_name) + { + attr.id = SAI_SCHEDULER_ATTR_MAX_BANDWIDTH_BURST_RATE; + attr.value.u64 = (uint64_t)stoi(fvValue(*i)); + sai_attr_list.push_back(attr); + } else { SWSS_LOG_ERROR("Unknown field:%s", fvField(*i).c_str()); return task_process_status::task_invalid_entry; @@ -910,7 +1009,7 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) sai_status = sai_scheduler_api->remove_scheduler(sai_object); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to remove scheduler profile. db name:%s sai object:%lx", qos_object_name.c_str(), sai_object); + SWSS_LOG_ERROR("Failed to remove scheduler profile. db name:%s sai object:%" PRIx64, qos_object_name.c_str(), sai_object); return task_process_status::task_failed; } auto it_to_delete = (m_qos_maps[qos_map_type_name])->find(qos_object_name); @@ -975,7 +1074,7 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(group_id, 1, &attr); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%lx of port:%s", group_id, port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%" PRIx64 " of port:%s", group_id, port.m_alias.c_str()); return SAI_NULL_OBJECT_ID; } @@ -994,7 +1093,7 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(group_id, 1, &attr); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%lx of port:%s", group_id, port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%" PRIx64 " of port:%s", group_id, port.m_alias.c_str()); return SAI_NULL_OBJECT_ID; } @@ -1028,7 +1127,7 @@ bool QosOrch::applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, const sai_object_id_t group_id = getSchedulerGroup(port, queue_id); if(group_id == SAI_NULL_OBJECT_ID) { - SWSS_LOG_ERROR("Failed to find a scheduler group for port: %s queue: %lu", port.m_alias.c_str(), queue_ind); + SWSS_LOG_ERROR("Failed to find a scheduler group for port: %s queue: %zu", port.m_alias.c_str(), queue_ind); return false; } @@ -1042,11 +1141,11 @@ bool QosOrch::applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, sai_status = sai_scheduler_group_api->set_scheduler_group_attribute(group_id, &attr); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed applying scheduler profile:0x%lx to scheduler group:0x%lx, port:%s", scheduler_profile_id, group_id, port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed applying scheduler profile:0x%" PRIx64 " to scheduler group:0x%" PRIx64 ", port:%s", scheduler_profile_id, group_id, port.m_alias.c_str()); return false; } - SWSS_LOG_DEBUG("port:%s, scheduler_profile_id:0x%lx applied to scheduler group:0x%lx", port.m_alias.c_str(), scheduler_profile_id, group_id); + SWSS_LOG_DEBUG("port:%s, scheduler_profile_id:0x%" PRIx64 " applied to scheduler group:0x%" PRIx64, port.m_alias.c_str(), scheduler_profile_id, group_id); return true; } @@ -1222,7 +1321,7 @@ bool QosOrch::applyMapToPort(Port &port, sai_attr_id_t attr_id, sai_object_id_t sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed setting sai object:%lx for port:%s, status:%d", map_id, port.m_alias.c_str(), status); + SWSS_LOG_ERROR("Failed setting sai object:%" PRIx64 " for port:%s, status:%d", map_id, port.m_alias.c_str(), status); return false; } return true; @@ -1386,7 +1485,7 @@ void QosOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index 9d8a81b8f0a..88ed96d9147 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -8,6 +8,7 @@ #include "portsorch.h" const string dscp_to_tc_field_name = "dscp_to_tc_map"; +const string dot1p_to_tc_field_name = "dot1p_to_tc_map"; const string pfc_to_pg_map_name = "pfc_to_pg_map"; const string pfc_to_queue_map_name = "pfc_to_queue_map"; const string pfc_enable_name = "pfc_enable"; @@ -36,6 +37,11 @@ const string scheduler_algo_STRICT = "STRICT"; const string scheduler_weight_field_name = "weight"; const string scheduler_priority_field_name = "priority"; +const string scheduler_min_bandwidth_rate_field_name = "cir";//Committed Information Rate +const string scheduler_min_bandwidth_burst_rate_field_name = "cbs";//Committed Burst Size +const string scheduler_max_bandwidth_rate_field_name = "pir";//Peak Information Rate +const string scheduler_max_bandwidth_burst_rate_field_name = "pbs";//Peak Burst Size + const string ecn_field_name = "ecn"; const string ecn_none = "ecn_none"; const string ecn_red = "ecn_red"; @@ -60,8 +66,15 @@ class QosMapHandler class DscpToTcMapHandler : public QosMapHandler { public: - bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes); - sai_object_id_t addQosItem(const vector &attributes); + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) override; + sai_object_id_t addQosItem(const vector &attributes) override; +}; + +class Dot1pToTcMapHandler : public QosMapHandler +{ +public: + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) override; + sai_object_id_t addQosItem(const vector &attributes) override; }; class TcToQueueMapHandler : public QosMapHandler @@ -129,6 +142,7 @@ class QosOrch : public Orch void initTableHandlers(); task_process_status handleDscpToTcTable(Consumer& consumer); + task_process_status handleDot1pToTcTable(Consumer& consumer); task_process_status handlePfcPrioToPgTable(Consumer& consumer); task_process_status handlePfcToQueueTable(Consumer& consumer); task_process_status handlePortQosMapTable(Consumer& consumer); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 44af8df3757..bff955b60da 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -1,4 +1,5 @@ #include +#include #include "routeorch.h" #include "logger.h" #include "swssnet.h" @@ -103,6 +104,91 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : m_syncdRoutes[v6_default_ip_prefix] = IpAddresses(); SWSS_LOG_NOTICE("Create IPv6 default route with packet action drop"); + + /* All the interfaces have the same MAC address and hence the same + * auto-generated link-local ipv6 address with eui64 interface-id. + * Hence add a single /128 route entry for the link-local interface + * address pointing to the CPU port. + */ + IpPrefix linklocal_prefix = getLinkLocalEui64Addr(); + + addLinkLocalRouteToMe(gVirtualRouterId, linklocal_prefix); + + /* Add fe80::/10 subnet route to forward all link-local packets + * destined to us, to CPU */ + IpPrefix default_link_local_prefix("fe80::/10"); + + addLinkLocalRouteToMe(gVirtualRouterId, default_link_local_prefix); + + /* TODO: Add the link-local fe80::/10 route to cpu in every VRF created from + * vrforch::addOperation. */ +} + +std::string RouteOrch::getLinkLocalEui64Addr(void) +{ + SWSS_LOG_ENTER(); + + string ip_prefix; + const uint8_t *gmac = gMacAddress.getMac(); + + uint8_t eui64_interface_id[EUI64_INTF_ID_LEN]; + char ipv6_ll_addr[INET6_ADDRSTRLEN] = {0}; + + /* Link-local IPv6 address autogenerated by kernel with eui64 interface-id + * derived from the MAC address of the host interface. + */ + eui64_interface_id[0] = gmac[0] ^ 0x02; + eui64_interface_id[1] = gmac[1]; + eui64_interface_id[2] = gmac[2]; + eui64_interface_id[3] = 0xff; + eui64_interface_id[4] = 0xfe; + eui64_interface_id[5] = gmac[3]; + eui64_interface_id[6] = gmac[4]; + eui64_interface_id[7] = gmac[5]; + + snprintf(ipv6_ll_addr, INET6_ADDRSTRLEN, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x", + eui64_interface_id[0], eui64_interface_id[1], eui64_interface_id[2], + eui64_interface_id[3], eui64_interface_id[4], eui64_interface_id[5], + eui64_interface_id[6], eui64_interface_id[7]); + + ip_prefix = string(ipv6_ll_addr); + + return ip_prefix; +} + +void RouteOrch::addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix) +{ + sai_route_entry_t unicast_route_entry; + unicast_route_entry.switch_id = gSwitchId; + unicast_route_entry.vr_id = vrf_id; + copy(unicast_route_entry.destination, linklocal_prefix); + subnet(unicast_route_entry.destination, unicast_route_entry.destination); + + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_FORWARD; + attrs.push_back(attr); + + Port cpu_port; + gPortsOrch->getCpuPort(cpu_port); + + attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + attr.value.oid = cpu_port.m_port_id; + attrs.push_back(attr); + + sai_status_t status = sai_route_api->create_route_entry(&unicast_route_entry, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create link local ipv6 route %s to cpu, rv:%d", + linklocal_prefix.getIp().to_string().c_str(), status); + throw runtime_error("Failed to create link local ipv6 route to cpu."); + } + + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); } bool RouteOrch::hasNextHopGroup(const IpAddresses& ipAddresses) const @@ -155,15 +241,14 @@ void RouteOrch::attach(Observer *observer, const IpAddress& dstAddr) observerEntry->second.observers.push_back(observer); - SWSS_LOG_NOTICE("Attached next hop observer of route %s for destination IP %s", - observerEntry->second.routeTable.rbegin()->first.to_string().c_str(), - dstAddr.to_string().c_str()); - // Trigger next hop change for the first time the observer is attached // Note that rbegin() is pointing to the entry with longest prefix match auto route = observerEntry->second.routeTable.rbegin(); if (route != observerEntry->second.routeTable.rend()) { + SWSS_LOG_NOTICE("Attached next hop observer of route %s for destination IP %s", + observerEntry->second.routeTable.rbegin()->first.to_string().c_str(), + dstAddr.to_string().c_str()); NextHopUpdate update = { dstAddr, route->first, route->second }; observer->update(SUBJECT_TYPE_NEXTHOP_CHANGE, static_cast(&update)); } @@ -238,7 +323,7 @@ bool RouteOrch::validnexthopinNextHopGroup(const IpAddress &ipaddr) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to add next hop member to group %lx: %d\n", + SWSS_LOG_ERROR("Failed to add next hop member to group %" PRIx64 ": %d\n", nhopgroup->second.next_hop_group_id, status); return false; } @@ -271,7 +356,7 @@ bool RouteOrch::invalidnexthopinNextHopGroup(const IpAddress &ipaddr) if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove next hop member %lx from group %lx: %d\n", + SWSS_LOG_ERROR("Failed to remove next hop member %" PRIx64 " from group %" PRIx64 ": %d\n", nexthop_id, nhopgroup->second.next_hop_group_id, status); return false; } @@ -286,7 +371,7 @@ void RouteOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -626,7 +711,7 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) if (status != SAI_STATUS_SUCCESS) { // TODO: do we need to clean up? - SWSS_LOG_ERROR("Failed to create next hop group %lx member %lx: %d\n", + SWSS_LOG_ERROR("Failed to create next hop group %" PRIx64 " member %" PRIx64 ": %d\n", next_hop_group_id, next_hop_group_member_id, status); return false; } @@ -677,7 +762,7 @@ bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) if (m_neighOrch->isNextHopFlagSet(nhop->first, NHFLAGS_IFDOWN)) { - SWSS_LOG_WARN("NHFLAGS_IFDOWN set for next hop group member %s with next_hop_id %lx", + SWSS_LOG_WARN("NHFLAGS_IFDOWN set for next hop group member %s with next_hop_id %" PRIx64, nhop->first.to_string().c_str(), nhop->second); nhop = next_hop_group_entry->second.nhopgroup_members.erase(nhop); continue; @@ -685,7 +770,7 @@ bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) status = sai_next_hop_group_api->remove_next_hop_group_member(nhop->second); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove next hop group member %lx, rv:%d", + SWSS_LOG_ERROR("Failed to remove next hop group member %" PRIx64 ", rv:%d", nhop->second, status); return false; } @@ -697,7 +782,7 @@ bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) status = sai_next_hop_group_api->remove_next_hop_group(next_hop_group_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove next hop group %lx, rv:%d", next_hop_group_id, status); + SWSS_LOG_ERROR("Failed to remove next hop group %" PRIx64 ", rv:%d", next_hop_group_id, status); return false; } diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index b4d87b69586..310e93cd87c 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -15,6 +15,9 @@ /* Maximum next hop group number */ #define NHGRP_MAX_SIZE 128 +/* Length of the Interface Id value in EUI64 format */ +#define EUI64_INTF_ID_LEN 8 + typedef std::map NextHopGroupMembers; struct NextHopGroupEntry @@ -84,6 +87,9 @@ class RouteOrch : public Orch, public Subject bool addRoute(IpPrefix, IpAddresses); bool removeRoute(IpPrefix); + std::string getLinkLocalEui64Addr(void); + void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); + void doTask(Consumer& consumer); }; diff --git a/orchagent/switchorch.cpp b/orchagent/switchorch.cpp index 4664b49bdb4..2a9341acff9 100644 --- a/orchagent/switchorch.cpp +++ b/orchagent/switchorch.cpp @@ -1,4 +1,5 @@ #include +#include #include "switchorch.h" #include "converter.h" @@ -200,9 +201,9 @@ bool SwitchOrch::setAgingFDB(uint32_t sec) auto status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set switch %lx fdb_aging_time attribute: %d", gSwitchId, status); + SWSS_LOG_ERROR("Failed to set switch %" PRIx64 " fdb_aging_time attribute: %d", gSwitchId, status); return false; } - SWSS_LOG_NOTICE("Set switch %lx fdb_aging_time %u sec", gSwitchId, sec); + SWSS_LOG_NOTICE("Set switch %" PRIx64 " fdb_aging_time %u sec", gSwitchId, sec); return true; } diff --git a/orchagent/tunneldecaporch.cpp b/orchagent/tunneldecaporch.cpp index f056d5753f1..a99c074993a 100644 --- a/orchagent/tunneldecaporch.cpp +++ b/orchagent/tunneldecaporch.cpp @@ -1,4 +1,5 @@ #include +#include #include "tunneldecaporch.h" #include "portsorch.h" #include "logger.h" @@ -21,7 +22,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -216,7 +217,7 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip return false; } - SWSS_LOG_NOTICE("Create overlay loopback router interface oid:%lx", overlayIfId); + SWSS_LOG_NOTICE("Create overlay loopback router interface oid:%" PRIx64, overlayIfId); // tunnel type (only ipinip for now) attr.id = SAI_TUNNEL_ATTR_TYPE; @@ -525,7 +526,7 @@ bool TunnelDecapOrch::removeDecapTunnel(string key) status = sai_tunnel_api->remove_tunnel(tunnel_info->tunnel_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove tunnel: %lu", tunnel_info->tunnel_id); + SWSS_LOG_ERROR("Failed to remove tunnel: %" PRIu64, tunnel_info->tunnel_id); return false; } @@ -533,7 +534,7 @@ bool TunnelDecapOrch::removeDecapTunnel(string key) status = sai_router_intfs_api->remove_router_interface(tunnel_info->overlay_intf_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove tunnel overlay interface: %lu", tunnel_info->overlay_intf_id); + SWSS_LOG_ERROR("Failed to remove tunnel overlay interface: %" PRIu64, tunnel_info->overlay_intf_id); return false; } @@ -558,7 +559,7 @@ bool TunnelDecapOrch::removeDecapTunnelTermEntry(sai_object_id_t tunnel_term_id, status = sai_tunnel_api->remove_tunnel_term_table_entry(tunnel_term_id); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove tunnel table entry: %lu", tunnel_term_id); + SWSS_LOG_ERROR("Failed to remove tunnel table entry: %" PRIu64, tunnel_term_id); return false; } diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index cc9be2decab..b1122f366ef 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "sai.h" #include "saiextensions.h" @@ -28,6 +29,7 @@ extern sai_neighbor_api_t* sai_neighbor_api; extern sai_next_hop_api_t* sai_next_hop_api; extern sai_bmtor_api_t* sai_bmtor_api; extern sai_object_id_t gSwitchId; +extern sai_object_id_t gVirtualRouterId; extern Directory gDirectory; extern PortsOrch *gPortsOrch; extern IntfsOrch *gIntfsOrch; @@ -100,10 +102,14 @@ bool VNetVrfObject::createObj(vector& attrs) for (auto vr_type : vr_cntxt) { - sai_object_id_t router_id; - if (vr_type != VR_TYPE::VR_INVALID && l_fn(router_id)) + sai_object_id_t router_id = gVirtualRouterId; + if (vr_type != VR_TYPE::VR_INVALID) { - SWSS_LOG_DEBUG("VNET vr_type %d router id %lx ", static_cast(vr_type), router_id); + if (getScope() != "default") + { + l_fn(router_id); + } + SWSS_LOG_DEBUG("VNET vr_type %d router id %" PRIx64 " ", static_cast(vr_type), router_id); vr_ids_.insert(std::pair(vr_type, router_id)); } } @@ -1393,6 +1399,7 @@ bool VNetOrch::addOperation(const Request& request) bool peer = false, create = false; uint32_t vni=0; string tunnel; + string scope; for (const auto& name: request.getAttrFieldNames()) { @@ -1416,6 +1423,10 @@ bool VNetOrch::addOperation(const Request& request) { tunnel = request.getAttrString("vxlan_tunnel"); } + else if (name == "scope") + { + scope = request.getAttrString("scope"); + } else { SWSS_LOG_INFO("Unknown attribute: %s", name.c_str()); @@ -1442,7 +1453,7 @@ bool VNetOrch::addOperation(const Request& request) if (it == std::end(vnet_table_)) { - VNetInfo vnet_info = { tunnel, vni, peer_list }; + VNetInfo vnet_info = { tunnel, vni, peer_list, scope }; obj = createObject(vnet_name, vnet_info, attrs); create = true; } @@ -1469,7 +1480,7 @@ bool VNetOrch::addOperation(const Request& request) if (it == std::end(vnet_table_)) { - VNetInfo vnet_info = { tunnel, vni, peer_list }; + VNetInfo vnet_info = { tunnel, vni, peer_list, scope }; obj = createObject(vnet_name, vnet_info, attrs); create = true; } @@ -1669,12 +1680,12 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP { if (op == SET_COMMAND && !add_route(vr_id, pfx, nh_id)) { - SWSS_LOG_ERROR("Route add failed for %s, vr_id '0x%lx", ipPrefix.to_string().c_str(), vr_id); + SWSS_LOG_ERROR("Route add failed for %s, vr_id '0x%" PRIx64, ipPrefix.to_string().c_str(), vr_id); return false; } else if (op == DEL_COMMAND && !del_route(vr_id, pfx)) { - SWSS_LOG_ERROR("Route del failed for %s, vr_id '0x%lx", ipPrefix.to_string().c_str(), vr_id); + SWSS_LOG_ERROR("Route del failed for %s, vr_id '0x%" PRIx64, ipPrefix.to_string().c_str(), vr_id); return false; } } diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index 3055ab92fa7..a7da8d90fe6 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -26,6 +26,7 @@ const request_description_t vnet_request_description = { { "vni", REQ_T_UINT }, { "peer_list", REQ_T_SET }, { "guid", REQ_T_STRING }, + { "scope", REQ_T_STRING }, }, { "vxlan_tunnel", "vni" } // mandatory attributes }; @@ -49,6 +50,7 @@ struct VNetInfo string tunnel; uint32_t vni; set peers; + string scope; }; typedef map vrid_list_t; @@ -73,7 +75,8 @@ class VNetObject VNetObject(const VNetInfo& vnetInfo) : tunnel_(vnetInfo.tunnel), peer_list_(vnetInfo.peers), - vni_(vnetInfo.vni) + vni_(vnetInfo.vni), + scope_(vnetInfo.scope) { } virtual bool updateObj(vector&) = 0; @@ -98,12 +101,18 @@ class VNetObject return vni_; } + string getScope() const + { + return scope_; + } + virtual ~VNetObject() noexcept(false) {}; private: set peer_list_ = {}; string tunnel_; uint32_t vni_; + string scope_; }; struct nextHop diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 8471a34aa45..e385c64c20c 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "sai.h" @@ -294,10 +295,17 @@ create_tunnel( void remove_tunnel(sai_object_id_t tunnel_id) { - sai_status_t status = sai_tunnel_api->remove_tunnel(tunnel_id); - if (status != SAI_STATUS_SUCCESS) + if (tunnel_id != SAI_NULL_OBJECT_ID) + { + sai_status_t status = sai_tunnel_api->remove_tunnel(tunnel_id); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove a tunnel object"); + } + } + else { - throw std::runtime_error("Can't remove a tunnel object"); + SWSS_LOG_DEBUG("Tunnel id is NULL."); } } @@ -363,10 +371,17 @@ create_tunnel_termination( void remove_tunnel_termination(sai_object_id_t term_table_id) { - sai_status_t status = sai_tunnel_api->remove_tunnel_term_table_entry(term_table_id); - if (status != SAI_STATUS_SUCCESS) + if (term_table_id != SAI_NULL_OBJECT_ID) + { + sai_status_t status = sai_tunnel_api->remove_tunnel_term_table_entry(term_table_id); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove a tunnel term table object"); + } + } + else { - throw std::runtime_error("Can't remove a tunnel term table object"); + SWSS_LOG_DEBUG("Tunnel term table id is NULL."); } } @@ -552,7 +567,7 @@ VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAd //Store the nh tunnel id tunnel_obj->updateNextHop(ipAddr, macAddress, vni, nh_id); - SWSS_LOG_INFO("NH vxlan tunnel was created for %s, id 0x%lx", tunnelName.c_str(), nh_id); + SWSS_LOG_INFO("NH vxlan tunnel was created for %s, id 0x%" PRIx64, tunnelName.c_str(), nh_id); return nh_id; } @@ -608,7 +623,7 @@ bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t tunnel_obj->insertMapperEntry(encap_id, decap_id, vni); - SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", encap_id, decap_id); + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%" PRIx64 "' decap entry '0x%" PRIx64 "'", encap_id, decap_id); } catch(const std::runtime_error& error) { @@ -651,7 +666,7 @@ bool VxlanTunnelOrch::removeVxlanTunnelMap(string tunnelName, uint32_t vni) remove_tunnel_map_entry(mapper.first); remove_tunnel_map_entry(mapper.second); - SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", mapper.first, mapper.second); + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%" PRIx64 "' decap entry '0x%" PRIx64 "'", mapper.first, mapper.second); } catch(const std::runtime_error& error) { @@ -895,7 +910,7 @@ bool VxlanVrfMapOrch::addOperation(const Request& request) entry.encap_id = tunnel_obj->addEncapMapperEntry(vrf_id, vni_id); entry.decap_id = tunnel_obj->addDecapMapperEntry(vrf_id, vni_id); - SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%" PRIx64 "' decap entry '0x%" PRIx64 "'", entry.encap_id, entry.decap_id); vxlan_vrf_table_[full_map_entry_name] = entry; diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index d4d3a60eea5..008175f92b5 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -121,7 +121,7 @@ class VxlanTunnel string tunnel_name_; bool active_ = false; - tunnel_ids_t ids_; + tunnel_ids_t ids_ = {0, 0, 0, 0}; std::pair tunnel_map_ = { MAP_T::MAP_TO_INVALID, MAP_T::MAP_TO_INVALID }; TunnelMapEntries tunnel_map_entries_; diff --git a/orchagent/watermarkorch.cpp b/orchagent/watermarkorch.cpp index 0cf746dcd48..0b8c20e9119 100644 --- a/orchagent/watermarkorch.cpp +++ b/orchagent/watermarkorch.cpp @@ -4,6 +4,7 @@ #include "notifier.h" #include "converter.h" #include "bufferorch.h" +#include #define DEFAULT_TELEMETRY_INTERVAL 120 @@ -50,7 +51,7 @@ void WatermarkOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -97,7 +98,7 @@ void WatermarkOrch::handleWmConfigUpdate(const std::string &key, const std::vect { if (i.first == "interval") { - auto intervT = timespec { .tv_sec = to_uint(i.second.c_str()) , .tv_nsec = 0 }; + auto intervT = timespec { .tv_sec = static_cast(to_uint(i.second.c_str())), .tv_nsec = 0 }; m_telemetryTimer->setInterval(intervT); // reset the timer interval when current timer expires m_timerChanged = true; @@ -141,7 +142,7 @@ void WatermarkOrch::handleFcConfigUpdate(const std::string &key, const std::vect void WatermarkOrch::doTask(NotificationConsumer &consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->isPortReady()) + if (!gPortsOrch->allPortsReady()) { return; } @@ -294,7 +295,7 @@ void WatermarkOrch::clearSingleWm(Table *table, string wm_name, vector vfvt = {{wm_name, "0"}}; diff --git a/orchagent/watermarkorch.h b/orchagent/watermarkorch.h index 27ac112eb15..35279efbab4 100644 --- a/orchagent/watermarkorch.h +++ b/orchagent/watermarkorch.h @@ -51,7 +51,7 @@ class WatermarkOrch : public Orch /* [7-2] - unused [1] - pg wm status - [0] - queue wm status (least significant bit) + [0] - queue wm status (least significant bit) */ uint8_t m_wmStatus = 0; bool m_timerChanged = false; diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index 41d2715dee5..20d41b7b8d9 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -95,20 +95,27 @@ LinkSync::LinkSync(DBConnector *appl_db, DBConnector *state_db) : if (!WarmStart::isWarmStart()) { /* See the comments for g_portSet in portsyncd.cpp */ - for (string port : g_portSet) + for (auto port_iter = g_portSet.begin(); port_iter != g_portSet.end();) { + string port = *port_iter; vector temp; + bool portFound = false; if (m_portTable.get(port, temp)) { for (auto it : temp) { if (fvField(it) == "admin_status") { - g_portSet.erase(port); + port_iter = g_portSet.erase(port_iter); + portFound = true; break; } } } + if (!portFound) + { + ++port_iter; + } } for (idx_p = if_ni; diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index 045922414f8..2b72d29ac76 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -79,10 +79,6 @@ int main(int argc, char **argv) WarmStart::checkWarmStart("portsyncd", "swss"); const bool warm = WarmStart::isWarmStart(); - LinkSync sync(&appl_db, &state_db); - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync); - try { NetLink netlink; @@ -102,6 +98,10 @@ int main(int argc, char **argv) } } + LinkSync sync(&appl_db, &state_db); + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync); + s.addSelectable(&netlink); s.addSelectable(&portCfg); @@ -163,10 +163,16 @@ int main(int argc, char **argv) } catch (const std::exception& e) { - cerr << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + cerr << "Exception \"" << e.what() << "\" was thrown in daemon" << endl; + return EXIT_FAILURE; + } + catch (...) + { + cerr << "Exception was thrown in daemon" << endl; return EXIT_FAILURE; } + return 1; } @@ -224,7 +230,7 @@ void handlePortConfigFile(ProducerStateTable &p, string file, bool warm) if (!infile.is_open()) { usage(); - throw "Port configuration file not found!"; + throw runtime_error("Port configuration file not found!"); } list header = {"name", "lanes", "alias", "speed", "autoneg", "fec"}; diff --git a/swssconfig/sample/00-copp.config.json b/swssconfig/sample/00-copp.config.json index 1fade2d172a..e82242830ce 100644 --- a/swssconfig/sample/00-copp.config.json +++ b/swssconfig/sample/00-copp.config.json @@ -34,8 +34,8 @@ "OP": "SET" }, { - "COPP_TABLE:trap.group.lldp.dhcp.udld": { - "trap_ids": "lldp,dhcp,udld", + "COPP_TABLE:trap.group.lldp.dhcp.dhcpv6.udld": { + "trap_ids": "lldp,dhcp,dhcpv6,udld", "trap_action":"trap", "trap_priority":"4", "queue": "4" diff --git a/teamsyncd/teamsync.cpp b/teamsyncd/teamsync.cpp index 2f180a10f74..d6f9555667e 100644 --- a/teamsyncd/teamsync.cpp +++ b/teamsyncd/teamsync.cpp @@ -127,10 +127,8 @@ void TeamSync::addLag(const string &lagName, int ifindex, bool admin_state, std::vector fvVector; FieldValueTuple a("admin_status", admin_state ? "up" : "down"); FieldValueTuple o("oper_status", oper_state ? "up" : "down"); - FieldValueTuple m("mtu", to_string(mtu)); fvVector.push_back(a); fvVector.push_back(o); - fvVector.push_back(m); m_lagTable.set(lagName, fvVector); SWSS_LOG_INFO("Add %s admin_status:%s oper_status:%s, mtu: %d", @@ -318,7 +316,8 @@ int TeamSync::TeamPortSync::getFd() return team_get_event_fd(m_team); } -void TeamSync::TeamPortSync::readData() +uint64_t TeamSync::TeamPortSync::readData() { team_handle_events(m_team); + return 0; } diff --git a/teamsyncd/teamsync.h b/teamsyncd/teamsync.h index 952dc8d4f3d..f47049d1a68 100644 --- a/teamsyncd/teamsync.h +++ b/teamsyncd/teamsync.h @@ -38,7 +38,7 @@ class TeamSync : public NetMsg ~TeamPortSync(); int getFd() override; - void readData() override; + uint64_t readData() override; /* member_name -> enabled|disabled */ std::map m_lagMembers; diff --git a/tests/conftest.py b/tests/conftest.py index a4d8ecb9e3a..c510e441cdc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,6 +23,8 @@ def pytest_addoption(parser): help="dvs name") parser.addoption("--keeptb", action="store_true", default=False, help="keep testbed after test") + parser.addoption("--imgname", action="store", default="docker-sonic-vs", + help="image name") class AsicDbValidator(object): def __init__(self, dvs): @@ -142,7 +144,7 @@ def runcmd_output(self, cmd): return subprocess.check_output("ip netns exec %s %s" % (self.nsname, cmd), shell=True) class DockerVirtualSwitch(object): - def __init__(self, name=None, keeptb=False, fakeplatform=None): + def __init__(self, name=None, imgname=None, keeptb=False, fakeplatform=None): self.basicd = ['redis-server', 'rsyslogd'] self.swssd = ['orchagent', @@ -158,6 +160,9 @@ def __init__(self, name=None, keeptb=False, fakeplatform=None): self.alld = self.basicd + self.swssd + self.syncd + self.rtd + self.teamd self.client = docker.from_env() + if subprocess.check_call(["/sbin/modprobe", "team"]) != 0: + raise NameError("cannot install kernel team module") + self.ctn = None if keeptb: self.cleanup = False @@ -211,7 +216,7 @@ def __init__(self, name=None, keeptb=False, fakeplatform=None): self.environment = ["fake_platform={}".format(fakeplatform)] if fakeplatform else [] # create virtual switch container - self.ctn = self.client.containers.run('docker-sonic-vs', privileged=True, detach=True, + self.ctn = self.client.containers.run(imgname, privileged=True, detach=True, environment=self.environment, network_mode="container:%s" % self.ctn_sw.name, volumes={ self.mount: { 'bind': '/var/run/redis', 'mode': 'rw' } }) @@ -791,8 +796,9 @@ def setReadOnlyAttr(self, obj, attr, val): def dvs(request): name = request.config.getoption("--dvsname") keeptb = request.config.getoption("--keeptb") + imgname = request.config.getoption("--imgname") fakeplatform = getattr(request.module, "DVS_FAKE_PLATFORM", None) - dvs = DockerVirtualSwitch(name, keeptb, fakeplatform) + dvs = DockerVirtualSwitch(name, imgname, keeptb, fakeplatform) yield dvs if name == None: dvs.get_logs(request.module.__name__) diff --git a/tests/test_acl.py b/tests/test_acl.py index 197026d548f..5cfeb49b4e2 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -3,18 +3,25 @@ import re import json -class TestAcl(object): + +class BaseTestAcl(object): + """ base class with helpers for Test classes """ def setup_db(self, dvs): self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) self.sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) - def create_acl_table(self, table, type, ports): + def create_acl_table(self, table, type, ports, stage=None): tbl = swsscommon.Table(self.cdb, "ACL_TABLE") - fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), - ("type", type), - ("ports", ",".join(ports))]) + table_props = [("policy_desc", "test"), + ("type", type), + ("ports", ",".join(ports))] + + if stage is not None: + table_props += [("stage", stage)] + + fvs = swsscommon.FieldValuePairs(table_props) tbl.set(table, fvs) time.sleep(1) @@ -124,6 +131,61 @@ def verify_acl_port_binding(self, dvs, adb, bind_ports): assert len(port_groups) == len(bind_ports) assert set(port_groups) == set(acl_table_groups) + def check_rule_existence(self, entry, rules, verifs): + """ helper function to verify if rule exists """ + + for rule in rules: + ruleD = dict(rule) + # find the rule to match with based on priority + if ruleD["PRIORITY"] == entry['SAI_ACL_ENTRY_ATTR_PRIORITY']: + ruleIndex = rules.index(rule) + # use verification dictionary to match entry to rule + for key in verifs[ruleIndex]: + assert verifs[ruleIndex][key] == entry[key] + return True + return False + + def create_acl_rule(self, table, rule, field, value): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "666"), + ("PACKET_ACTION", "FORWARD"), + (field, value)]) + tbl.set(table + "|" + rule, fvs) + time.sleep(1) + + def remove_acl_rule(self, table, rule): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table + "|" + rule) + time.sleep(1) + + def verify_acl_rule(self, dvs, field, value): + acl_table_id = self.get_acl_table_id(dvs) + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + acl_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entries) == 1 + + (status, fvs) = tbl.get(acl_entries[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "666" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_FORWARD" + elif fv[0] == field: + assert fv[1] == value + else: + assert False + + +class TestAcl(BaseTestAcl): def test_AclTableCreation(self, dvs, testlog): self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) @@ -923,21 +985,6 @@ def test_V6AclTableDeletion(self, dvs, testlog): # only the default table was left assert len(keys) >= 1 - #helper function to verify if rule exists - def check_rule_existence(self, entry, rules, verifs): - for rule in rules: - ruleD = dict(rule) - #find the rule to match with based on priority - if ruleD["PRIORITY"] == entry['SAI_ACL_ENTRY_ATTR_PRIORITY']: - ruleIndex = rules.index(rule) - #use verification dictionary to match entry to rule - for key in verifs[ruleIndex]: - assert verifs[ruleIndex][key] == entry[key] - #found the rule - return True - #did not find the rule - return False - def test_InsertAclRuleBetweenPriorities(self, dvs, testlog): self.setup_db(dvs) db = swsscommon.DBConnector(4, dvs.redis_sock, 0) @@ -1123,45 +1170,6 @@ def test_RulesWithDiffMaskLengths(self, dvs, testlog): keys = atbl.getKeys() assert len(keys) >= 1 - def create_acl_rule(self, table, rule, field, value): - tbl = swsscommon.Table(self.cdb, "ACL_RULE") - fvs = swsscommon.FieldValuePairs([("priority", "666"), - ("PACKET_ACTION", "FORWARD"), - (field, value)]) - tbl.set(table + "|" + rule, fvs) - time.sleep(1) - - def remove_acl_rule(self, table, rule): - tbl = swsscommon.Table(self.cdb, "ACL_RULE") - tbl._del(table + "|" + rule) - time.sleep(1) - - def verify_acl_rule(self, dvs, field, value): - acl_table_id = self.get_acl_table_id(dvs) - - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") - acl_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] - assert len(acl_entries) == 1 - - (status, fvs) = tbl.get(acl_entries[0]) - assert status == True - assert len(fvs) == 6 - for fv in fvs: - if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": - assert fv[1] == acl_table_id - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": - assert fv[1] == "true" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": - assert fv[1] == "666" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": - assert True - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": - assert fv[1] == "SAI_PACKET_ACTION_FORWARD" - elif fv[0] == field: - assert fv[1] == value - else: - assert False - def test_AclRuleIcmp(self, dvs, testlog): self.setup_db(dvs) @@ -1206,3 +1214,98 @@ def test_AclRuleIcmpV6(self, dvs, testlog): self.remove_acl_rule(acl_table, acl_rule) self.remove_acl_table(acl_table) + + +class TestAclRuleValidation(BaseTestAcl): + """ Test class for cases that check if orchagent corectly validates + ACL rules input + """ + + SWITCH_CAPABILITY_TABLE = "SWITCH_CAPABILITY" + + def get_acl_actions_supported(self, stage): + capability_table = swsscommon.Table(self.sdb, self.SWITCH_CAPABILITY_TABLE) + keys = capability_table.getKeys() + # one switch available + assert len(keys) == 1 + status, fvs = capability_table.get(keys[0]) + assert status == True + + field = "ACL_ACTIONS|{}".format(stage.upper()) + fvs = dict(fvs) + + values_list = fvs.get(field, None) + + if values_list is not None: + values_list = values_list.split(",") + + return values_list + + def test_AclActionValidation(self, dvs, testlog): + """ The test overrides R/O SAI_SWITCH_ATTR_ACL_STAGE_INGRESS/EGRESS switch attributes + to check the case when orchagent refuses to process rules with action that is not supported + by the ASIC + """ + + self.setup_db(dvs) + + stage_name_map = { + "ingress": "SAI_SWITCH_ATTR_ACL_STAGE_INGRESS", + "egress": "SAI_SWITCH_ATTR_ACL_STAGE_EGRESS", + } + + for stage in stage_name_map: + action_values = self.get_acl_actions_supported(stage) + + # virtual switch supports all actions + assert action_values is not None + assert "PACKET_ACTION" in action_values + + sai_acl_stage = stage_name_map[stage] + + # mock switch attribute in VS so only REDIRECT action is supported on this stage + dvs.setReadOnlyAttr("SAI_OBJECT_TYPE_SWITCH", + sai_acl_stage, + # FIXME: here should use sai_serialize_value() for acl_capability_t + # but it is not available in VS testing infrastructure + "false:1:SAI_ACL_ACTION_TYPE_REDIRECT") + + # restart SWSS so orchagent will query updated switch attributes + dvs.stop_swss() + dvs.start_swss() + # wait for daemons to start + time.sleep(2) + # reinit ASIC DB validator object + dvs.init_asicdb_validator() + + action_values = self.get_acl_actions_supported(stage) + # now, PACKET_ACTION is not supported + # and REDIRECT_ACTION is supported + assert "PACKET_ACTION" not in action_values + assert "REDIRECT_ACTION" in action_values + + # try to create a forward rule + + acl_table = "TEST_TABLE" + acl_rule = "TEST_RULE" + + bind_ports = ["Ethernet0", "Ethernet4"] + + self.create_acl_table(acl_table, "L3", bind_ports, stage=stage) + self.create_acl_rule(acl_table, acl_rule, "ICMP_TYPE", "8") + + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + # verify there are no non-default ACL rules created + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 0 + + self.remove_acl_table(acl_table) + # remove rules from CFG DB + self.remove_acl_rule(acl_table, acl_rule) + + dvs.runcmd("supervisorctl restart syncd") + dvs.stop_swss() + dvs.start_swss() + time.sleep(5) diff --git a/tests/test_acl_egress_table.py b/tests/test_acl_egress_table.py index 2795737e6b7..e1d1f4f861b 100644 --- a/tests/test_acl_egress_table.py +++ b/tests/test_acl_egress_table.py @@ -1,340 +1,340 @@ -from swsscommon import swsscommon - -import time - -class TestEgressAclTable(object): - def setup_db(self, dvs): - self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) - self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - - def create_egress_acl_table(self, table_name, ports): - tbl = swsscommon.Table(self.cdb, "ACL_TABLE") - - fvs = swsscommon.FieldValuePairs([("POLICY_DESC", "EGRESS_ACL_TEST"), - ("TYPE", "L3"), - ("PORTS", ports), - ("stage", "EGRESS")]) - tbl.set(table_name, fvs) - time.sleep(1) - - def create_acl_rule(self, fv_pairs, rule_name): - rule_tbl = swsscommon.Table(self.cdb, "ACL_RULE") - fvs = swsscommon.FieldValuePairs(fv_pairs) - rule_tbl.set("egress_acl_table|" + rule_name, fvs) - time.sleep(1) - - def remove_acl_table(self, table_name): - tbl = swsscommon.Table(self.cdb, "ACL_TABLE") - tbl._del(table_name) - time.sleep(1) - - def get_acl_table_id(self, dvs): - atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - keys = atbl.getKeys() - for k in dvs.asicdb.default_acl_tables: - assert k in keys - acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_tables] - - assert len(acl_tables) == 1 - - return acl_tables[0] - - def remove_acl_rule(self, table_name, rule_name): - tbl = swsscommon.Table(self.cdb, "ACL_RULE") - tbl._del(table_name + "|" + rule_name) - time.sleep(1) - - def verify_acl_asic_table(self, dvs, bind_ports): - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") - acl_table_groups = tbl.getKeys() - assert len(acl_table_groups) == len(bind_ports) - - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - port_groups = [] - for p in [dvs.asicdb.portnamemap[portname] for portname in bind_ports]: - (status, fvs) = tbl.get(p) - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_EGRESS_ACL": - assert fv[1] in acl_table_groups - port_groups.append(fv[1]) - - assert len(port_groups) == len(bind_ports) - assert set(port_groups) == set(acl_table_groups) - - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") - for port_group in port_groups: - (status, fvs) = tbl.get(port_group) - assert status == True - assert len(fvs) == 3 - for fv in fvs: - if fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE": - assert fv[1] == "SAI_ACL_STAGE_EGRESS" - elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_BIND_POINT_TYPE_LIST": - assert fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_PORT" - elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_TYPE": - assert fv[1] == "SAI_ACL_TABLE_GROUP_TYPE_PARALLEL" - else: - assert False - - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") - member = tbl.getKeys()[0] - (status, fvs) = tbl.get(member) - assert status == True - assert len(fvs) == 3 - for fv in fvs: - if fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": - assert fv[1] in port_groups - elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": - table_id = fv[1] - elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": - assert fv[1] == "100" - else: - assert False - - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - (status, fvs) = tbl.get(table_id) - assert status == True - - def verify_acl_rule_asic_fvs(self, dvs, fv_tuple): - # Verify Acl entry in ASIC DB - test_acl_table_id = self.get_acl_table_id(dvs) - acl_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") - keys = acl_tbl.getKeys() - - acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] - assert len(acl_entry) == 1 - - (status, fvs) = acl_tbl.get(acl_entry[0]) - assert status == True - assert len(fvs) == 6 - for fv in fvs: - if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": - assert fv[1] == test_acl_table_id - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": - assert fv[1] == "true" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": - assert fv[1] == "1000" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": - assert True - elif fv[0] == fv_tuple[0]: - assert fv[1] == fv_tuple[1] - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": - assert fv[1] == "SAI_PACKET_ACTION_DROP" - else: - assert False - - def verify_acl_rule_with_L4PortRange_asic_fvs(self, dvs, fv_tuple): - # Verify Acl entry in ASIC DB - test_acl_table_id = self.get_acl_table_id(dvs) - acl_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") - keys = acl_tbl.getKeys() - - acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] - assert len(acl_entry) == 1 - - (status, fvs) = acl_tbl.get(acl_entry[0]) - assert status == True - assert len(fvs) == 6 - for fv in fvs: - if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": - assert fv[1] == test_acl_table_id - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": - assert fv[1] == "true" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": - assert fv[1] == "999" - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": - assert True - elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE": - aclrange = fv[1] - elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": - assert fv[1] == "SAI_PACKET_ACTION_FORWARD" - else: - assert False - - # Verify Acl range in ASIC DB - acl_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE") - aclrange_obj = aclrange.split(":", 1)[1] - - (status, fvs) = acl_tbl.get(aclrange_obj) - assert status == True - assert len(fvs) == 2 - for fv in fvs: - if fv[0] == "SAI_ACL_RANGE_ATTR_TYPE": - assert fv[1] == fv_tuple[0] - elif fv[0] == "SAI_ACL_RANGE_ATTR_LIMIT": - assert fv[1] == fv_tuple[1] - else: - assert False - - def check_asic_table_absent(self, dvs): - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - acl_tables = tbl.getKeys() - for key in dvs.asicdb.default_acl_tables: - assert key in acl_tables - acl_tables = [k for k in acl_tables if k not in dvs.asicdb.default_acl_tables] - - assert len(acl_tables) == 0 - - def check_asic_rule_absent(self, dvs): - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") - acl_entries = tbl.getKeys() - for key in dvs.asicdb.default_acl_entries: - assert key in acl_entries - acl_entries = [k for k in acl_entries if k not in dvs.asicdb.default_acl_entries] - - assert len(acl_entries) == 0 - - def test_EgressAclTableCreation(self, dvs): - self.setup_db(dvs) - - # Create ACL_TABLE in config db - bind_ports = ["Ethernet0", "Ethernet4"] - self.create_egress_acl_table("egress_acl_table", ",".join(bind_ports)) - - time.sleep(1) - - # Check acl table in asic db - self.verify_acl_asic_table(dvs, bind_ports) - - def test_EgressAclRuleL4SrcPortRange(self, dvs): - self.setup_db(dvs) - - # Create L4 SrcPortRange Acl rule - fvPairs = [("priority", "999"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT_RANGE", "0-1001")] - self.create_acl_rule(fvPairs, "L4SrcPortRange_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE", "0,1001") - self.verify_acl_rule_with_L4PortRange_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "L4SrcPortRange_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclRuleL4DstPortRange(self, dvs): - self.setup_db(dvs) - - # Create L4 DstPortRange Acl rule - fvPairs = [("priority", "999"), ("PACKET_ACTION", "FORWARD"), ("L4_DST_PORT_RANGE", "1003-6666")] - self.create_acl_rule(fvPairs, "L4DstPortRange_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE", "1003,6666") - self.verify_acl_rule_with_L4PortRange_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "L4DstPortRange_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclRuleL2EthType(self, dvs): - self.setup_db(dvs) - - # Create L4 L2EthType Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("ETHER_TYPE", "8000")] - self.create_acl_rule(fvPairs, "L2EthType_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE", "8000&mask:0xffff") - self.verify_acl_rule_asic_fvs( dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "L2EthType_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclRuleTunnelVNI(self, dvs): - self.setup_db(dvs) - - # Create Tunnel VNI Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("TUNNEL_VNI", "5000")] - self.create_acl_rule(fvPairs, "TunnelVNI_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_VNI", "5000&mask:0xffffffff") - self.verify_acl_rule_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "TunnelVNI_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclRuleTC(self, dvs): - self.setup_db(dvs) - - # Create TC Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("TC", "1")] - self.create_acl_rule(fvPairs, "TC_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_TC", "1&mask:0xff") - self.verify_acl_rule_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "TC_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclInnerIPProtocol(self, dvs): - self.setup_db(dvs) - - # Create InnerIPProtocol Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_IP_PROTOCOL", "8")] - self.create_acl_rule(fvPairs, "InnerIPProtocol_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_IP_PROTOCOL", "8&mask:0xff") - self.verify_acl_rule_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "InnerIPProtocol_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclInnerEthType(self, dvs): - self.setup_db(dvs) - - # Create InnerEthernetType Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_ETHER_TYPE", "8000")] - self.create_acl_rule(fvPairs, "InnerEthType_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_ETHER_TYPE", "8000&mask:0xffff") - self.verify_acl_rule_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "InnerEthType_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclInnerL4SrcPort(self, dvs): - self.setup_db(dvs) - - # Create InnerL4SrcPort Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_L4_SRC_PORT", "999")] - self.create_acl_rule(fvPairs, "InnerL4SrcPort_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_SRC_PORT", "999&mask:0xffff") - self.verify_acl_rule_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "InnerL4SrcPort_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclInnerL4DstPort(self, dvs): - self.setup_db(dvs) - - # Create InnerL4DstPort Acl rule - fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_L4_DST_PORT", "999")] - self.create_acl_rule(fvPairs, "InnerL4DstPort_rule") - - # Verify Acl rule in ASIC DB - fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_DST_PORT", "999&mask:0xffff") - self.verify_acl_rule_asic_fvs(dvs, fv_tuple) - - # Remove Acl rule - self.remove_acl_rule("egress_acl_table", "InnerL4DstPort_rule") - self.check_asic_rule_absent(dvs) - - def test_EgressAclTableDeletion(self, dvs): - self.setup_db(dvs) - - # Remove Acl table - self.remove_acl_table("egress_acl_table") - self.check_asic_table_absent(dvs) +from swsscommon import swsscommon + +import time + +class TestEgressAclTable(object): + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + def create_egress_acl_table(self, table_name, ports): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + + fvs = swsscommon.FieldValuePairs([("POLICY_DESC", "EGRESS_ACL_TEST"), + ("TYPE", "L3"), + ("PORTS", ports), + ("stage", "EGRESS")]) + tbl.set(table_name, fvs) + time.sleep(1) + + def create_acl_rule(self, fv_pairs, rule_name): + rule_tbl = swsscommon.Table(self.cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs(fv_pairs) + rule_tbl.set("egress_acl_table|" + rule_name, fvs) + time.sleep(1) + + def remove_acl_table(self, table_name): + tbl = swsscommon.Table(self.cdb, "ACL_TABLE") + tbl._del(table_name) + time.sleep(1) + + def get_acl_table_id(self, dvs): + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + for k in dvs.asicdb.default_acl_tables: + assert k in keys + acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_tables] + + assert len(acl_tables) == 1 + + return acl_tables[0] + + def remove_acl_rule(self, table_name, rule_name): + tbl = swsscommon.Table(self.cdb, "ACL_RULE") + tbl._del(table_name + "|" + rule_name) + time.sleep(1) + + def verify_acl_asic_table(self, dvs, bind_ports): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_groups = tbl.getKeys() + assert len(acl_table_groups) == len(bind_ports) + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + port_groups = [] + for p in [dvs.asicdb.portnamemap[portname] for portname in bind_ports]: + (status, fvs) = tbl.get(p) + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_EGRESS_ACL": + assert fv[1] in acl_table_groups + port_groups.append(fv[1]) + + assert len(port_groups) == len(bind_ports) + assert set(port_groups) == set(acl_table_groups) + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + for port_group in port_groups: + (status, fvs) = tbl.get(port_group) + assert status == True + assert len(fvs) == 3 + for fv in fvs: + if fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE": + assert fv[1] == "SAI_ACL_STAGE_EGRESS" + elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_BIND_POINT_TYPE_LIST": + assert fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_PORT" + elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_TYPE": + assert fv[1] == "SAI_ACL_TABLE_GROUP_TYPE_PARALLEL" + else: + assert False + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") + member = tbl.getKeys()[0] + (status, fvs) = tbl.get(member) + assert status == True + assert len(fvs) == 3 + for fv in fvs: + if fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": + assert fv[1] in port_groups + elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": + table_id = fv[1] + elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": + assert fv[1] == "100" + else: + assert False + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + (status, fvs) = tbl.get(table_id) + assert status == True + + def verify_acl_rule_asic_fvs(self, dvs, fv_tuple): + # Verify Acl entry in ASIC DB + test_acl_table_id = self.get_acl_table_id(dvs) + acl_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = acl_tbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = acl_tbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1000" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == fv_tuple[0]: + assert fv[1] == fv_tuple[1] + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + def verify_acl_rule_with_L4PortRange_asic_fvs(self, dvs, fv_tuple): + # Verify Acl entry in ASIC DB + test_acl_table_id = self.get_acl_table_id(dvs) + acl_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = acl_tbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = acl_tbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "999" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE": + aclrange = fv[1] + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_FORWARD" + else: + assert False + + # Verify Acl range in ASIC DB + acl_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE") + aclrange_obj = aclrange.split(":", 1)[1] + + (status, fvs) = acl_tbl.get(aclrange_obj) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "SAI_ACL_RANGE_ATTR_TYPE": + assert fv[1] == fv_tuple[0] + elif fv[0] == "SAI_ACL_RANGE_ATTR_LIMIT": + assert fv[1] == fv_tuple[1] + else: + assert False + + def check_asic_table_absent(self, dvs): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + acl_tables = tbl.getKeys() + for key in dvs.asicdb.default_acl_tables: + assert key in acl_tables + acl_tables = [k for k in acl_tables if k not in dvs.asicdb.default_acl_tables] + + assert len(acl_tables) == 0 + + def check_asic_rule_absent(self, dvs): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + acl_entries = tbl.getKeys() + for key in dvs.asicdb.default_acl_entries: + assert key in acl_entries + acl_entries = [k for k in acl_entries if k not in dvs.asicdb.default_acl_entries] + + assert len(acl_entries) == 0 + + def test_EgressAclTableCreation(self, dvs): + self.setup_db(dvs) + + # Create ACL_TABLE in config db + bind_ports = ["Ethernet0", "Ethernet4"] + self.create_egress_acl_table("egress_acl_table", ",".join(bind_ports)) + + time.sleep(1) + + # Check acl table in asic db + self.verify_acl_asic_table(dvs, bind_ports) + + def test_EgressAclRuleL4SrcPortRange(self, dvs): + self.setup_db(dvs) + + # Create L4 SrcPortRange Acl rule + fvPairs = [("priority", "999"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT_RANGE", "0-1001")] + self.create_acl_rule(fvPairs, "L4SrcPortRange_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE", "0,1001") + self.verify_acl_rule_with_L4PortRange_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "L4SrcPortRange_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclRuleL4DstPortRange(self, dvs): + self.setup_db(dvs) + + # Create L4 DstPortRange Acl rule + fvPairs = [("priority", "999"), ("PACKET_ACTION", "FORWARD"), ("L4_DST_PORT_RANGE", "1003-6666")] + self.create_acl_rule(fvPairs, "L4DstPortRange_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE", "1003,6666") + self.verify_acl_rule_with_L4PortRange_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "L4DstPortRange_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclRuleL2EthType(self, dvs): + self.setup_db(dvs) + + # Create L4 L2EthType Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("ETHER_TYPE", "8000")] + self.create_acl_rule(fvPairs, "L2EthType_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE", "8000&mask:0xffff") + self.verify_acl_rule_asic_fvs( dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "L2EthType_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclRuleTunnelVNI(self, dvs): + self.setup_db(dvs) + + # Create Tunnel VNI Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("TUNNEL_VNI", "5000")] + self.create_acl_rule(fvPairs, "TunnelVNI_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_VNI", "5000&mask:0xffffffff") + self.verify_acl_rule_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "TunnelVNI_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclRuleTC(self, dvs): + self.setup_db(dvs) + + # Create TC Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("TC", "1")] + self.create_acl_rule(fvPairs, "TC_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_TC", "1&mask:0xff") + self.verify_acl_rule_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "TC_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclInnerIPProtocol(self, dvs): + self.setup_db(dvs) + + # Create InnerIPProtocol Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_IP_PROTOCOL", "8")] + self.create_acl_rule(fvPairs, "InnerIPProtocol_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_IP_PROTOCOL", "8&mask:0xff") + self.verify_acl_rule_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "InnerIPProtocol_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclInnerEthType(self, dvs): + self.setup_db(dvs) + + # Create InnerEthernetType Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_ETHER_TYPE", "8000")] + self.create_acl_rule(fvPairs, "InnerEthType_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_ETHER_TYPE", "8000&mask:0xffff") + self.verify_acl_rule_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "InnerEthType_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclInnerL4SrcPort(self, dvs): + self.setup_db(dvs) + + # Create InnerL4SrcPort Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_L4_SRC_PORT", "999")] + self.create_acl_rule(fvPairs, "InnerL4SrcPort_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_SRC_PORT", "999&mask:0xffff") + self.verify_acl_rule_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "InnerL4SrcPort_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclInnerL4DstPort(self, dvs): + self.setup_db(dvs) + + # Create InnerL4DstPort Acl rule + fvPairs = [("priority", "1000"), ("PACKET_ACTION", "DROP"), ("INNER_L4_DST_PORT", "999")] + self.create_acl_rule(fvPairs, "InnerL4DstPort_rule") + + # Verify Acl rule in ASIC DB + fv_tuple = ("SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_DST_PORT", "999&mask:0xffff") + self.verify_acl_rule_asic_fvs(dvs, fv_tuple) + + # Remove Acl rule + self.remove_acl_rule("egress_acl_table", "InnerL4DstPort_rule") + self.check_asic_rule_absent(dvs) + + def test_EgressAclTableDeletion(self, dvs): + self.setup_db(dvs) + + # Remove Acl table + self.remove_acl_table("egress_acl_table") + self.check_asic_table_absent(dvs) diff --git a/tests/test_crm.py b/tests/test_crm.py index bd4db74c5f5..425f471cb4b 100644 --- a/tests/test_crm.py +++ b/tests/test_crm.py @@ -17,6 +17,24 @@ def getCrmCounterValue(dvs, key, counter): return 0 +def getCrmConfigValue(dvs, key, counter): + + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + crm_stats_table = swsscommon.Table(config_db, 'CRM') + + for k in crm_stats_table.get(key)[1]: + if k[0] == counter: + return int(k[1]) + +def getCrmConfigStr(dvs, key, counter): + + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + crm_stats_table = swsscommon.Table(config_db, 'CRM') + + for k in crm_stats_table.get(key)[1]: + if k[0] == counter: + return k[1] + return "" def setReadOnlyAttr(dvs, obj, attr, val): @@ -40,583 +58,870 @@ def setReadOnlyAttr(dvs, obj, attr, val): ntf.send("set_ro", key, fvp) +def check_syslog(dvs, marker, err_log, expected_cnt): + (exitcode, num) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep \"%s\" | wc -l" % (marker, err_log)]) + assert num.strip() == str(expected_cnt) -def test_CrmFdbEntry(dvs, testlog): +class TestCrm(object): + def test_CrmFdbEntry(self, dvs, testlog): - # disable ipv6 on Ethernet8 neighbor as once ipv6 link-local address is - # configured, server 2 will send packet which can switch to learn another - # mac and fail the test. - dvs.servers[2].runcmd("sysctl -w net.ipv6.conf.eth0.disable_ipv6=1") - dvs.runcmd("crm config polling interval 1") + # disable ipv6 on Ethernet8 neighbor as once ipv6 link-local address is + # configured, server 2 will send packet which can switch to learn another + # mac and fail the test. + dvs.servers[2].runcmd("sysctl -w net.ipv6.conf.eth0.disable_ipv6=1") + dvs.runcmd("crm config polling interval 1") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') - time.sleep(2) + time.sleep(2) - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - # create a FDB entry - tbl = swsscommon.ProducerStateTable(app_db, "FDB_TABLE") - fvs = swsscommon.FieldValuePairs([("port","Ethernet8"),("type","dynamic")]) - tbl.set("Vlan2:52-54-00-25-06-E9", fvs) + # create a FDB entry + tbl = swsscommon.ProducerStateTable(app_db, "FDB_TABLE") + fvs = swsscommon.FieldValuePairs([("port","Ethernet8"),("type","dynamic")]) + tbl.set("Vlan2:52-54-00-25-06-E9", fvs) - # create vlan - tbl = swsscommon.Table(cfg_db, "VLAN") - fvs = swsscommon.FieldValuePairs([("vlanid", "2")]) - tbl.set("Vlan2", fvs) + # create vlan + tbl = swsscommon.Table(cfg_db, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", "2")]) + tbl.set("Vlan2", fvs) - # create vlan member - tbl = swsscommon.Table(cfg_db, "VLAN_MEMBER") - fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) - tbl.set("Vlan2|Ethernet8", fvs) + # create vlan member + tbl = swsscommon.Table(cfg_db, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + tbl.set("Vlan2|Ethernet8", fvs) - # update available counter - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '999') + # update available counter + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '999') - time.sleep(2) + time.sleep(2) - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - # update available counter - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') + # update available counter + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') - time.sleep(2) + time.sleep(2) - # get counters - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') + # get counters + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') - assert new_avail_counter == avail_counter + assert new_avail_counter == avail_counter - # enable ipv6 on server 2 - dvs.servers[2].runcmd("sysctl -w net.ipv6.conf.eth0.disable_ipv6=0") + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds fdb high 90") + dvs.runcmd("crm config thresholds fdb type free") + time.sleep(2) + check_syslog(dvs, marker, "FDB_ENTRY THRESHOLD_EXCEEDED for TH_FREE", 1) -def test_CrmIpv4Route(dvs, testlog): + # enable ipv6 on server 2 + dvs.servers[2].runcmd("sysctl -w net.ipv6.conf.eth0.disable_ipv6=0") - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - dvs.runcmd("ifconfig Ethernet0 up") + def test_CrmIpv4Route(self, dvs, testlog): - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + dvs.runcmd("ifconfig Ethernet0 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - # add static neighbor - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) + # add static neighbor + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - time.sleep(2) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') + time.sleep(2) - # add route and update available counter - ps.set("2.2.2.0/24", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - time.sleep(2) + # add route and update available counter + ps.set("2.2.2.0/24", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - # remove route and update available counter - ps._del("2.2.2.0/24") - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove route and update available counter + ps._del("2.2.2.0/24") + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - intf_tbl._del("Ethernet0|10.0.0.0/31") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds ipv4 route high 90") + dvs.runcmd("crm config thresholds ipv4 route type free") + time.sleep(2) + check_syslog(dvs, marker, "IPV4_ROUTE THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmIpv6Route(dvs, testlog): + intf_tbl._del("Ethernet0|10.0.0.0/31") + time.sleep(2) - # Enable IPv6 routing - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - time.sleep(2) + def test_CrmIpv6Route(self, dvs, testlog): - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet0|fc00::1/126", fvs) - dvs.runcmd("ifconfig Ethernet0 up") + # Enable IPv6 routing + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + time.sleep(2) - dvs.servers[0].runcmd("ifconfig eth0 inet6 add fc00::2/126") - dvs.servers[0].runcmd("ip -6 route add default via fc00::1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet0|fc00::1/126", fvs) + dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("crm config polling interval 1") + dvs.servers[0].runcmd("ifconfig eth0 inet6 add fc00::2/126") + dvs.servers[0].runcmd("ip -6 route add default via fc00::1") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - # get neighbor and arp entry - dvs.servers[0].runcmd("ping6 -c 4 fc00::1") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","fc00::2"), ("ifname", "Ethernet0")]) + # get neighbor and arp entry + dvs.servers[0].runcmd("ping6 -c 4 fc00::1") - time.sleep(2) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","fc00::2"), ("ifname", "Ethernet0")]) - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') + time.sleep(2) - # add route and update available counter - ps.set("2001::/64", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') - time.sleep(2) + # add route and update available counter + ps.set("2001::/64", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') - # remove route and update available counter - ps._del("2001::/64") - dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove route and update available counter + ps._del("2001::/64") + dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') - intf_tbl._del("Ethernet0|fc00::1/126") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds ipv6 route high 90") + dvs.runcmd("crm config thresholds ipv6 route type free") + time.sleep(2) + check_syslog(dvs, marker, "IPV6_ROUTE THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmIpv4Nexthop(dvs, testlog): + intf_tbl._del("Ethernet0|fc00::1/126") + time.sleep(2) - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet0", fvs) - dvs.runcmd("ifconfig Ethernet0 up") + def test_CrmIpv4Nexthop(self, dvs, testlog): - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet0", fvs) + dvs.runcmd("ifconfig Ethernet0 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - time.sleep(2) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') + time.sleep(2) - # add nexthop and update available counter - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') - time.sleep(2) + # add nexthop and update available counter + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') - # remove nexthop and update available counter - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove nexthop and update available counter + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') - intf_tbl._del("Ethernet0|10.0.0.0/31") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds ipv4 nexthop high 90") + dvs.runcmd("crm config thresholds ipv4 nexthop type free") + time.sleep(2) + check_syslog(dvs, marker, "IPV4_NEXTHOP THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmIpv6Nexthop(dvs, testlog): + intf_tbl._del("Ethernet0|10.0.0.0/31") + time.sleep(2) - # Enable IPv6 routing - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - time.sleep(2) + def test_CrmIpv6Nexthop(self, dvs, testlog): - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet0|fc00::1/126", fvs) - dvs.runcmd("ifconfig Ethernet0 up") + # Enable IPv6 routing + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + time.sleep(2) - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet0|fc00::1/126", fvs) + dvs.runcmd("ifconfig Ethernet0 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - time.sleep(2) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') + time.sleep(2) - # add nexthop and update available counter - dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') - time.sleep(2) + # add nexthop and update available counter + dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') - # remove nexthop and update available counter - dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove nexthop and update available counter + dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') - intf_tbl._del("Ethernet0|fc00::1/126") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds ipv6 nexthop high 90") + dvs.runcmd("crm config thresholds ipv6 nexthop type free") + time.sleep(2) + check_syslog(dvs, marker, "IPV6_NEXTHOP THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmIpv4Neighbor(dvs, testlog): + intf_tbl._del("Ethernet0|fc00::1/126") + time.sleep(2) - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - dvs.runcmd("ifconfig Ethernet0 up") + def test_CrmIpv4Neighbor(self, dvs, testlog): - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + dvs.runcmd("ifconfig Ethernet0 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - time.sleep(2) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') + time.sleep(2) - # add neighbor and update available counter - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') - time.sleep(2) + # add neighbor and update available counter + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') - # remove neighbor and update available counter - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove neighbor and update available counter + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') - intf_tbl._del("Ethernet0|10.0.0.0/31") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds ipv4 neighbor high 90") + dvs.runcmd("crm config thresholds ipv4 neighbor type free") + time.sleep(2) + check_syslog(dvs, marker, "IPV4_NEIGHBOR THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmIpv6Neighbor(dvs, testlog): + intf_tbl._del("Ethernet0|10.0.0.0/31") + time.sleep(2) - # Enable IPv6 routing - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - time.sleep(2) + def test_CrmIpv6Neighbor(self, dvs, testlog): - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet0|fc00::1/126", fvs) - dvs.runcmd("ifconfig Ethernet0 up") + # Enable IPv6 routing + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + time.sleep(2) - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet0|fc00::1/126", fvs) + dvs.runcmd("ifconfig Ethernet0 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - time.sleep(2) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') + time.sleep(2) - # add neighbor and update available counter - dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') - time.sleep(2) + # add neighbor and update available counter + dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') - # remove neighbor and update available counter - dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove neighbor and update available counter + dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') - intf_tbl._del("Ethernet0|fc00::1/126") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds ipv6 neighbor high 90") + dvs.runcmd("crm config thresholds ipv6 neighbor type free") + time.sleep(2) + check_syslog(dvs, marker, "IPV6_NEIGHBOR THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmNexthopGroup(dvs, testlog): + intf_tbl._del("Ethernet0|fc00::1/126") + time.sleep(2) - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") + def test_CrmNexthopGroup(self, dvs, testlog): - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - # add neighbors - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) + # add neighbors + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - time.sleep(2) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') + time.sleep(2) - # add route and update available counter - ps.set("2.2.2.0/24", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '999') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') - time.sleep(2) + # add route and update available counter + ps.set("2.2.2.0/24", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '999') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') + time.sleep(2) - assert new_used_counter - used_counter == 1 - assert avail_counter - new_avail_counter == 1 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') - # remove route and update available counter - ps._del("2.2.2.0/24") - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 - time.sleep(2) + # remove route and update available counter + ps._del("2.2.2.0/24") + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') - intf_tbl._del("Ethernet0|10.0.0.0/31") - intf_tbl._del("Ethernet4|10.0.0.2/31") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds nexthop group member high 90") + dvs.runcmd("crm config thresholds nexthop group object type free") + time.sleep(2) + check_syslog(dvs, marker, "NEXTHOP_GROUP THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmNexthopGroupMember(dvs, testlog): + intf_tbl._del("Ethernet0|10.0.0.0/31") + intf_tbl._del("Ethernet4|10.0.0.2/31") + time.sleep(2) - # down, then up to generate port up signal - dvs.servers[0].runcmd("ip link set down dev eth0") == 0 - dvs.servers[1].runcmd("ip link set down dev eth0") == 0 - dvs.servers[0].runcmd("ip link set up dev eth0") == 0 - dvs.servers[1].runcmd("ip link set up dev eth0") == 0 + def test_CrmNexthopGroupMember(self, dvs, testlog): - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") + # down, then up to generate port up signal + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[1].runcmd("ip link set down dev eth0") == 0 + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[1].runcmd("ip link set up dev eth0") == 0 - dvs.runcmd("crm config polling interval 1") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') + dvs.runcmd("crm config polling interval 1") - # add neighbors - dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) + # add neighbors + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - time.sleep(2) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) - # get counters - used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') - avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') + time.sleep(2) - # add route and update available counter - ps.set("2.2.2.0/24", fvs) - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '998') + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') - time.sleep(2) + # add route and update available counter + ps.set("2.2.2.0/24", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '998') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') + time.sleep(2) - assert new_used_counter - used_counter == 2 - assert avail_counter - new_avail_counter == 2 + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') - # remove route and update available counter - ps._del("2.2.2.0/24") - dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") - setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') + assert new_used_counter - used_counter == 2 + assert avail_counter - new_avail_counter == 2 - time.sleep(2) + # remove route and update available counter + ps._del("2.2.2.0/24") + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') - # get counters - new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') - new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') + time.sleep(2) - assert new_used_counter == used_counter - assert new_avail_counter == avail_counter + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') - intf_tbl._del("Ethernet0|10.0.0.0/31") - intf_tbl._del("Ethernet4|10.0.0.2/31") - time.sleep(2) + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + marker = dvs.add_log_marker() + dvs.runcmd("crm config polling interval 2") + dvs.runcmd("crm config thresholds nexthop group member high 90") + dvs.runcmd("crm config thresholds nexthop group member type free") + time.sleep(2) + check_syslog(dvs, marker, "NEXTHOP_GROUP_MEMBER THRESHOLD_EXCEEDED for TH_FREE",1) -def test_CrmAcl(dvs, testlog): + intf_tbl._del("Ethernet0|10.0.0.0/31") + intf_tbl._del("Ethernet4|10.0.0.2/31") + time.sleep(2) - db = swsscommon.DBConnector(4, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + def test_CrmAcl(self, dvs, testlog): - dvs.runcmd("crm config polling interval 1") - time.sleep(1) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - bind_ports = ["Ethernet0", "Ethernet4"] + dvs.runcmd("crm config polling interval 1") + time.sleep(1) - old_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + bind_ports = ["Ethernet0", "Ethernet4"] - # create ACL table - ttbl = swsscommon.Table(db, "ACL_TABLE") - fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) - ttbl.set("test", fvs) + old_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') - # create ACL rule - rtbl = swsscommon.Table(db, "ACL_RULE") - fvs = swsscommon.FieldValuePairs([("priority", "55"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT", "65000")]) - rtbl.set("test|acl_test_rule", fvs) + # create ACL table + ttbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + ttbl.set("test", fvs) - time.sleep(2) - - new_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') - table_used_counter = new_table_used_counter - old_table_used_counter - assert table_used_counter == 1 + # create ACL rule + rtbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "55"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT", "65000")]) + rtbl.set("test|acl_test_rule", fvs) - # get ACL table key - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") - acl_tables = [k for k in atbl.getKeys() if k not in dvs.asicdb.default_acl_tables] - key = "ACL_TABLE_STATS:{0}".format(acl_tables[0].replace('oid:', '')) + time.sleep(2) - entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') - assert entry_used_counter == 1 + new_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + table_used_counter = new_table_used_counter - old_table_used_counter + assert table_used_counter == 1 - cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') - assert entry_used_counter == 1 + # get ACL table key + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + acl_tables = [k for k in atbl.getKeys() if k not in dvs.asicdb.default_acl_tables] + key = "ACL_TABLE_STATS:{0}".format(acl_tables[0].replace('oid:', '')) - # remove ACL rule - rtbl._del("test|acl_test_rule") + entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') + assert entry_used_counter == 1 - time.sleep(2) + cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') + assert entry_used_counter == 1 - entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') - assert entry_used_counter == 0 + # remove ACL rule + rtbl._del("test|acl_test_rule") - cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') - assert cnt_used_counter == 0 + time.sleep(2) - # remove ACL table - ttbl._del("test") + entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') + assert entry_used_counter == 0 - time.sleep(2) + cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') + assert cnt_used_counter == 0 - new_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') - table_used_counter = new_table_used_counter - old_table_used_counter - assert table_used_counter == 0 + # remove ACL table + ttbl._del("test") - counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) - crm_stats_table = swsscommon.Table(counters_db, 'CRM') - keys = crm_stats_table.getKeys() - assert key not in keys + time.sleep(2) + + new_table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + table_used_counter = new_table_used_counter - old_table_used_counter + assert table_used_counter == 0 + + counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + crm_stats_table = swsscommon.Table(counters_db, 'CRM') + keys = crm_stats_table.getKeys() + assert key not in keys + + def test_CrmAclGroup(self, dvs, testlog): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + dvs.runcmd("crm config polling interval 1") + bind_ports = ["Ethernet0", "Ethernet4", "Ethernet8"] + + # create ACL table + tbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "testv6"), ("type", "L3V6"), ("ports", ",".join(bind_ports))]) + tbl.set("test-aclv6", fvs) + + time.sleep(2) + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + entry_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_group_used') + assert entry_used_counter == 3 + + # remove ACL table + #tbl._del("test-aclv6") + #time.sleep(2) + #atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + #table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_group_used') + #assert table_used_counter == 0 + + def test_Configure(self, dvs, testlog): + + #polling interval + dvs.runcmd("crm config polling interval 10") + time.sleep(2) + polling_interval = getCrmConfigValue(dvs, 'Config', 'polling_interval') + assert polling_interval == 10 + + def test_Configure_ipv4_route(self, dvs, testlog): + + #ipv4 route low/high threshold/type + dvs.runcmd("crm config thresholds ipv4 route low 50") + dvs.runcmd("crm config thresholds ipv4 route high 90") + dvs.runcmd("crm config thresholds ipv4 route type percentage") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv4_route_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv4_route_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv4_route_threshold_type') + assert threshold_type == 'percentage' + + def test_Configure_ipv6_route(self, dvs, testlog): + + #ipv6 route low/high threshold/type + dvs.runcmd("crm config thresholds ipv6 route low 50") + dvs.runcmd("crm config thresholds ipv6 route high 90") + dvs.runcmd("crm config thresholds ipv6 route type used") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv6_route_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv6_route_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv6_route_threshold_type') + assert threshold_type == 'used' + + def test_Configure_ipv4_nexthop(self, dvs, testlog): + + #ipv4 nexthop low/high threshold/type + dvs.runcmd("crm config thresholds ipv4 nexthop low 50") + dvs.runcmd("crm config thresholds ipv4 nexthop high 90") + dvs.runcmd("crm config thresholds ipv4 nexthop type 'percentage'") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv4_nexthop_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv4_nexthop_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv4_nexthop_threshold_type') + assert threshold_type == 'percentage' + + def test_Configure_ipv6_nexthop(self, dvs, testlog): + + #ipv6 nexthop low/high threshold/type + dvs.runcmd("crm config thresholds ipv6 nexthop low 50") + dvs.runcmd("crm config thresholds ipv6 nexthop high 90") + dvs.runcmd("crm config thresholds ipv6 nexthop type free") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv6_nexthop_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv6_nexthop_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv6_nexthop_threshold_type') + assert threshold_type == 'free' + + def test_Configure_ipv4_neighbor(self, dvs, testlog): + + #ipv4 neighbor low/high threshold/type + dvs.runcmd("crm config thresholds ipv4 neighbor low 50") + dvs.runcmd("crm config thresholds ipv4 neighbor high 90") + dvs.runcmd("crm config thresholds ipv4 neighbor type percentage") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv4_neighbor_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv4_neighbor_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv4_neighbor_threshold_type') + assert threshold_type == 'percentage' + + def test_Configure_ipv6_neighbor(self, dvs, testlog): + + #ipv6 neighbor low/high threshold/type + dvs.runcmd("crm config thresholds ipv6 neighbor low 50") + dvs.runcmd("crm config thresholds ipv6 neighbor high 90") + dvs.runcmd("crm config thresholds ipv6 neighbor type used") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv6_neighbor_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv6_neighbor_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv6_neighbor_threshold_type') + assert threshold_type == 'used' + + def test_Configure_group_member(self, dvs, testlog): + + #nexthop group member low/high threshold/type + dvs.runcmd("crm config thresholds nexthop group member low 50") + dvs.runcmd("crm config thresholds nexthop group member high 90") + dvs.runcmd("crm config thresholds nexthop group member type percentage") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'nexthop_group_member_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'nexthop_group_member_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'nexthop_group_member_threshold_type') + assert threshold_type == 'percentage' + + def test_Configure_group_object(self, dvs, testlog): + + #nexthop group object low/high threshold/type + dvs.runcmd("crm config thresholds nexthop group object low 50") + dvs.runcmd("crm config thresholds nexthop group object high 90") + dvs.runcmd("crm config thresholds nexthop group object type free") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'nexthop_group_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'nexthop_group_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'nexthop_group_threshold_type') + assert threshold_type == 'free' + + def test_Configure_acl_table(self, dvs, testlog): + + #thresholds acl table low/high threshold/type + dvs.runcmd("crm config thresholds acl table low 50") + dvs.runcmd("crm config thresholds acl table high 90") + dvs.runcmd("crm config thresholds acl table type percentage") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_table_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_table_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_table_threshold_type') + assert threshold_type == 'percentage' + + def test_Configure_acl_group(self, dvs, testlog): + + #thresholds acl group low/high threshold/type + dvs.runcmd("crm config thresholds acl group low 50") + dvs.runcmd("crm config thresholds acl group high 90") + dvs.runcmd("crm config thresholds acl group type used") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_group_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_group_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_group_threshold_type') + assert threshold_type == 'used' + + def test_Configure_acl_group_entry(self, dvs, testlog): + + #thresholds acl group entry low/high threshold/type + dvs.runcmd("crm config thresholds acl group entry low 50") + dvs.runcmd("crm config thresholds acl group entry high 90") + dvs.runcmd("crm config thresholds acl group entry type percentage") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_entry_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_entry_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_entry_threshold_type') + assert threshold_type == 'percentage' + + def test_Configure_acl_group_counter(self, dvs, testlog): + + #thresholds acl group counter low/high threshold/type + dvs.runcmd("crm config thresholds acl group counter low 50") + dvs.runcmd("crm config thresholds acl group counter high 90") + dvs.runcmd("crm config thresholds acl group counter type free") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_counter_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_counter_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_counter_threshold_type') + assert threshold_type == 'free' + + def test_Configure_fdb(self, dvs, testlog): + + #thresholds fdb low/high threshold/type + dvs.runcmd("crm config thresholds fdb low 50") + dvs.runcmd("crm config thresholds fdb high 90") + dvs.runcmd("crm config thresholds fdb type percentage") + + time.sleep(2) + threshold_low = getCrmConfigValue(dvs, 'Config', 'fdb_entry_low_threshold') + assert threshold_low == 50 + threshold_high = getCrmConfigValue(dvs, 'Config', 'fdb_entry_high_threshold') + assert threshold_high == 90 + threshold_type = getCrmConfigStr(dvs, 'Config', 'fdb_entry_threshold_type') + assert threshold_type == 'percentage' diff --git a/tests/test_dirbcast.py b/tests/test_dirbcast.py index 12068812ca0..4f966d44cd9 100644 --- a/tests/test_dirbcast.py +++ b/tests/test_dirbcast.py @@ -3,90 +3,91 @@ import re import json -def test_DirectedBroadcast(dvs, testlog): +class TestDirectedBroadcast(object): + def test_DirectedBroadcast(self, dvs, testlog): - db = swsscommon.DBConnector(4, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - # create vlan in config db - tbl = swsscommon.Table(db, "VLAN") - fvs = swsscommon.FieldValuePairs([("vlanid", "100")]) - tbl.set("Vlan100", fvs) + # create vlan in config db + tbl = swsscommon.Table(db, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", "100")]) + tbl.set("Vlan100", fvs) - # create a vlan member in config db - tbl = swsscommon.Table(db, "VLAN_MEMBER") - fvs = swsscommon.FieldValuePairs([("tagging_mode", "tagged")]) - tbl.set("Vlan100|Ethernet24", fvs) + # create a vlan member in config db + tbl = swsscommon.Table(db, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "tagged")]) + tbl.set("Vlan100|Ethernet24", fvs) - time.sleep(1) + time.sleep(1) - # create vlan interface in config db - tbl = swsscommon.Table(db, "VLAN_INTERFACE") - fvs = swsscommon.FieldValuePairs([("family", "IPv4")]) - tbl.set("Vlan100", fvs) - tbl.set("Vlan100|192.169.0.1/27", fvs) + # create vlan interface in config db + tbl = swsscommon.Table(db, "VLAN_INTERFACE") + fvs = swsscommon.FieldValuePairs([("family", "IPv4")]) + tbl.set("Vlan100", fvs) + tbl.set("Vlan100|192.169.0.1/27", fvs) - time.sleep(1) + time.sleep(1) - # check vlan in asic db - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - keys = atbl.getKeys() - vlan_oid = None + # check vlan in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + keys = atbl.getKeys() + vlan_oid = None - for key in keys: - if key == dvs.asicdb.default_vlan_id: - continue + for key in keys: + if key == dvs.asicdb.default_vlan_id: + continue - (status, fvs) = atbl.get(key) - assert status == True + (status, fvs) = atbl.get(key) + assert status == True - if fvs[0][0] == "SAI_VLAN_ATTR_VLAN_ID": - assert fvs[0][1] == '100' - vlan_oid = key + if fvs[0][0] == "SAI_VLAN_ATTR_VLAN_ID": + assert fvs[0][1] == '100' + vlan_oid = key - assert vlan_oid != None + assert vlan_oid != None - # check router interface in asic db - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") - keys = atbl.getKeys() - rif_oid = None + # check router interface in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + keys = atbl.getKeys() + rif_oid = None - for key in keys: - (status, fvs) = atbl.get(key) - assert status == True - for fv in fvs: - if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": - assert vlan_oid == fv[1] - rif_oid = key + for key in keys: + (status, fvs) = atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert vlan_oid == fv[1] + rif_oid = key - assert rif_oid != None + assert rif_oid != None - # check neighbor entry in asic db - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") - keys = atbl.getKeys() - dir_bcast = False + # check neighbor entry in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + keys = atbl.getKeys() + dir_bcast = False - for key in keys: - neigh = json.loads(key) + for key in keys: + neigh = json.loads(key) - if neigh['ip'] == "192.169.0.31": - dir_bcast = True - assert neigh['rif'] == rif_oid - (status, fvs) = atbl.get(key) - assert status == True - if fvs[0][0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": - assert fvs[0][1] == "FF:FF:FF:FF:FF:FF" + if neigh['ip'] == "192.169.0.31": + dir_bcast = True + assert neigh['rif'] == rif_oid + (status, fvs) = atbl.get(key) + assert status == True + if fvs[0][0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": + assert fvs[0][1] == "FF:FF:FF:FF:FF:FF" - assert dir_bcast + assert dir_bcast - # Explicitly add a neighbor entry with BCAST MAC and check if its in ASIC_DB - dvs.runcmd("ip neigh replace 192.169.0.30 lladdr FF:FF:FF:FF:FF:FF dev Vlan100") + # Explicitly add a neighbor entry with BCAST MAC and check if its in ASIC_DB + dvs.runcmd("ip neigh replace 192.169.0.30 lladdr FF:FF:FF:FF:FF:FF dev Vlan100") - time.sleep(1) + time.sleep(1) - keys = atbl.getKeys() - for key in keys: - neigh = json.loads(key) + keys = atbl.getKeys() + for key in keys: + neigh = json.loads(key) - if neigh['ip'] == "192.169.0.30": - assert False + if neigh['ip'] == "192.169.0.30": + assert False diff --git a/tests/test_fdb.py b/tests/test_fdb.py new file mode 100644 index 00000000000..4e714d2021b --- /dev/null +++ b/tests/test_fdb.py @@ -0,0 +1,374 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +class TestFdb(object): + def test_FdbWarmRestartNotifications(self, dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + + dvs.runcmd("crm config polling interval 1") + dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') + + time.sleep(2) + counter_before = dvs.getCrmCounterValue('STATS', 'crm_stats_fdb_entry_used') + + # create vlan; create vlan member + dvs.create_vlan("6") + dvs.create_vlan_member("6", "Ethernet64") + dvs.create_vlan_member("6", "Ethernet68") + + # Put Ethernet72 and Ethernet76 into vlan 7 in untagged mode, they will have pvid of 7 + # and into vlan8 in tagged mode + dvs.create_vlan("7") + dvs.create_vlan_member("7", "Ethernet72") + dvs.create_vlan_member("7", "Ethernet76") + + dvs.create_vlan("8") + dvs.create_vlan_member_tagged("8", "Ethernet72") + dvs.create_vlan_member_tagged("8", "Ethernet76") + + + # Get mapping between interface name and its bridge port_id + time.sleep(2) + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check FDB learning mode + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet64"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet68"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet72"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet76"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + + # check fdb aging attr + ok, extra = dvs.all_table_entry_has_no(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH', + ".*", + ["SAI_SWITCH_ATTR_FDB_AGING_TIME"]) + + # bring up vlan and member + dvs.set_interface_status("Vlan6", "up") + dvs.set_interface_status("Vlan7", "up") + dvs.set_interface_status("Vlan8", "up") + + dvs.add_ip_address("Vlan6", "6.6.6.1/24") + dvs.add_ip_address("Vlan7", "7.7.7.1/24") + dvs.add_ip_address("Vlan8", "8.8.8.1/24") + + dvs.set_interface_status("Ethernet64", "up") + dvs.set_interface_status("Ethernet68", "up") + dvs.set_interface_status("Ethernet72", "up") + dvs.set_interface_status("Ethernet76", "up") + dvs.servers[16].runcmd("ifconfig eth0 6.6.6.6/24 up") + dvs.servers[16].runcmd("ip route add default via 6.6.6.1") + dvs.servers[17].runcmd("ifconfig eth0 6.6.6.7/24 up") + dvs.servers[17].runcmd("ip route add default via 6.6.6.1") + + dvs.servers[18].runcmd("vconfig add eth0 8") + dvs.servers[18].runcmd("ifconfig eth0.8 8.8.8.6/24 up") + dvs.servers[18].runcmd("ip route add default via 8.8.8.1") + + dvs.servers[19].runcmd("vconfig add eth0 8") + dvs.servers[19].runcmd("ifconfig eth0.8 8.8.8.7/24 up") + dvs.servers[19].runcmd("ip route add default via 8.8.8.1") + + dvs.servers[18].runcmd("ifconfig eth0 7.7.7.6/24 up") + dvs.servers[18].runcmd("ip route add default via 7.7.7.1") + dvs.servers[19].runcmd("ifconfig eth0 7.7.7.7/24 up") + dvs.servers[19].runcmd("ip route add default via 7.7.7.1") + + # get neighbor and arp entry + time.sleep(2) + rc = dvs.servers[16].runcmd("ping -c 1 6.6.6.7") + assert rc == 0 + rc = dvs.servers[17].runcmd("ping -c 1 6.6.6.6") + assert rc == 0 + + # get neighbor and arp entry + time.sleep(2) + rc = dvs.servers[18].runcmd("ping -c 1 8.8.8.7") + assert rc == 0 + rc = dvs.servers[19].runcmd("ping -c 1 8.8.8.6") + assert rc == 0 + + time.sleep(2) + rc = dvs.servers[18].runcmd("ping -c 1 -I 7.7.7.6 7.7.7.7") + assert rc == 0 + rc = dvs.servers[19].runcmd("ping -c 1 -I 7.7.7.7 7.7.7.6") + assert rc == 0 + + # check that the FDB entries were inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet64"]), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet68"]), + ] + ) + assert ok, str(extra) + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet72"]), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet76"]), + ] + ) + assert ok, str(extra) + + time.sleep(2) + counter_inserted = dvs.getCrmCounterValue('STATS', 'crm_stats_fdb_entry_used') + # vlan 6: Ethernet64, Ethernet68; + # vlan 7: Ethernet72, Ethernet76; + # vlan 8 (tagged): Ethernet72, Ethernet76; + # 6 FDB entries wil be created in total + assert counter_inserted - counter_before == 6 + + # check that the FDB entries were inserted into State DB for Ethernet64, Ethernet68 with Vlan6 + ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan6:.*", + [("port", "Ethernet64"), + ("type", "dynamic"), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan6:*", + [("port", "Ethernet68"), + ("type", "dynamic"), + ] + ) + assert ok, str(extra) + + # check that the FDB entries were inserted into State DB, + # Vlan7(untagged) in the key for Ethernet72, Ethernet76 + ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan7:.*", + [("port", "Ethernet72"), + ("type", "dynamic"), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan7:*", + [("port", "Ethernet76"), + ("type", "dynamic"), + ] + ) + assert ok, str(extra) + + # check that the FDB entries were inserted into State DB, + # Vlan8 (tagged) in the key for Ethernet72, Ethernet76 + ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan8:.*", + [("port", "Ethernet72"), + ("type", "dynamic"), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan8:*", + [("port", "Ethernet76"), + ("type", "dynamic"), + ] + ) + assert ok, str(extra) + + # enable warm restart + (exitcode, result) = dvs.runcmd("config warm_restart enable swss") + assert exitcode == 0 + + # freeze orchagent for warm restart + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK succeeded\n" + time.sleep(2) + + try: + # restart orchagent + dvs.stop_swss() + + # check FDB learning mode + ok, extra = dvs.all_table_entry_has(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + ".*", + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE")]) + assert ok, str(extra) + # check FDB aging time + ok, extra = dvs.all_table_entry_has(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH', + ".*", + [("SAI_SWITCH_ATTR_FDB_AGING_TIME", "0")]) + assert ok, str(extra) + + dvs.start_swss() + time.sleep(2) + + # Get mapping between interface name and its bridge port_id + # Note: they are changed + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entries were inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet64"]), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet68"]), + ] + ) + assert ok, str(extra) + + # check that the FDB entries were inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet72"]), + ] + ) + assert ok, str(extra) + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet76"]), + ] + ) + assert ok, str(extra) + + # check FDB learning mode + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet64"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet68"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet72"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', + iface_2_bridge_port_id["Ethernet76"], + [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) + assert ok, str(extra) + + time.sleep(2) + counter_restarted = dvs.getCrmCounterValue('STATS', 'crm_stats_fdb_entry_used') + assert counter_inserted == counter_restarted + + # check fdb aging attr + ok, extra = dvs.all_table_entry_has_no(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH', + ".*", + ["SAI_SWITCH_ATTR_FDB_AGING_TIME"]) + + finally: + # disable warm restart + dvs.runcmd("config warm_restart disable swss") + # slow down crm polling + dvs.runcmd("crm config polling interval 10000") + + def test_FdbAddedAfterMemberCreated(self, dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52-54-00-25-06-E9"), ("vlan", "2")], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] + ) + assert ok, str(extra) + + dvs.runcmd("sonic-clear fdb all") + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_cold.py b/tests/test_fdb_cold.py deleted file mode 100644 index 8ebe7bf8165..00000000000 --- a/tests/test_fdb_cold.py +++ /dev/null @@ -1,80 +0,0 @@ -from swsscommon import swsscommon -import os -import sys -import time -import json -from distutils.version import StrictVersion - -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - - # FIXME: better to wait until DB create them - time.sleep(1) - -def create_entry_tbl(db, table, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def create_entry_pst(db, table, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - -def test_FDBAddedAfterMemberCreated(dvs, testlog): - dvs.setup_db() - - dvs.runcmd("sonic-clear fdb all") - time.sleep(2) - - # create a FDB entry in Application DB - create_entry_pst( - dvs.pdb, - "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", - [ - ("port", "Ethernet0"), - ("type", "dynamic"), - ] - ) - - # check that the FDB entry wasn't inserted into ASIC DB - assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" - - vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") - vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") - - # create vlan - dvs.create_vlan("2") - dvs.create_vlan_member("2", "Ethernet0") - - # check that the vlan information was propagated - vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") - vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") - - assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" - assert bp_after - bp_before == 1, "The bridge port wasn't created" - assert vm_after - vm_before == 1, "The vlan member wasn't added" - - # Get mapping between interface name and its bridge port_id - iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) - - # check that the FDB entry was inserted into ASIC DB - assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" - - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [("mac", "52-54-00-25-06-E9"), ("vlan", "2")], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), - ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] - ) - assert ok, str(extra) - - dvs.runcmd("sonic-clear fdb all") - dvs.remove_vlan_member("2", "Ethernet0") - dvs.remove_vlan("2") - diff --git a/tests/test_fdb_update.py b/tests/test_fdb_update.py new file mode 100644 index 00000000000..b3fd1a9670d --- /dev/null +++ b/tests/test_fdb_update.py @@ -0,0 +1,261 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) +def remove_entry(tbl, key): + tbl._del(key) + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def remove_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + remove_entry(tbl, key) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def remove_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + remove_entry(tbl, key) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def get_mac_by_bridge_id(dvs, bridge_id): + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") + keys = tbl.getKeys() + + mac = [] + for key in keys: + (status, fvs) = tbl.get(key) + assert status + value = dict(fvs) + if value["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"] == bridge_id: + try: + d_key = json.loads(key) + except ValueError: + d_key = json.loads('{' + key + '}') + mac.append(d_key["mac"]) + return mac + +def test_FDBAddedAndUpdated(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + create_entry_tbl( + dvs.cdb, + "VLAN", "Vlan2", + [ + ("vlanid", "2"), + ] + ) + + # create vlan member entry in Config db + create_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet0", + [ + ("tagging_mode", "untagged"), + ] + ) + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52-54-00-25-06-E9"), ("vlan", "2")], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] + ) + assert ok, str(extra) + + # create vlan member entry in Config DB + create_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet4", + [ + ("tagging_mode", "untagged"), + ] + ) + + # update FDB entry port in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet4"), + ("type", "dynamic"), + ] + ) + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52-54-00-25-06-E9"), ("vlan", "2")], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet4"]), + ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] + ) + assert ok, str(extra) + + # remove FDB entry from Application DB + remove_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9" + ) + + # remove vlan member entry from Config DB + remove_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet4" + ) + remove_entry_tbl( + dvs.cdb, + "VLAN_MEMBER", "Vlan2|Ethernet0" + ) + + #remove vlan entry from Config DB + remove_entry_tbl( + dvs.cdb, + "VLAN", "Vlan2" + ) + +def test_FDBLearnedAndUpdated(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + + # create vlan; create vlan member + dvs.create_vlan("6") + dvs.create_vlan_member("6", "Ethernet64") + dvs.create_vlan_member("6", "Ethernet68") + dvs.create_vlan_member("6", "Ethernet72") + + # Get mapping between interface name and its bridge port_id + time.sleep(2) + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # bring up vlan and member + dvs.set_interface_status("Vlan6", "up") + + dvs.add_ip_address("Vlan6", "6.6.6.1/24") + + dvs.set_interface_status("Ethernet64", "up") + dvs.set_interface_status("Ethernet68", "up") + dvs.set_interface_status("Ethernet72", "up") + + dvs.servers[16].runcmd("ifconfig eth0 hw ether 00:00:00:00:00:11") + dvs.servers[16].runcmd("ifconfig eth0 6.6.6.6/24 up") + dvs.servers[16].runcmd("ip route add default via 6.6.6.1") + dvs.servers[17].runcmd("ifconfig eth0 6.6.6.7/24 up") + dvs.servers[17].runcmd("ip route add default via 6.6.6.1") + time.sleep(2) + + # get neighbor and arp entry + rc = dvs.servers[16].runcmd("ping -c 1 6.6.6.7") + assert rc == 0 + rc = dvs.servers[17].runcmd("ping -c 1 6.6.6.6") + assert rc == 0 + time.sleep(2) + + # check that the FDB entries were inserted into ASIC DB + Ethernet64_mac = get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet64"]) + assert "00:00:00:00:00:11" in Ethernet64_mac + + # update FDB entry port in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan6:00-00-00-00-00-11", + [ + ("port", "Ethernet72"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was updated in ASIC DB + Ethernet72_mac = get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet72"]) + assert "00:00:00:00:00:11" in Ethernet72_mac, "Updating fdb entry to Ethernet72 failed" + + Ethernet64_mac = get_mac_by_bridge_id(dvs, iface_2_bridge_port_id["Ethernet64"]) + assert "00:00:00:00:00:11" not in Ethernet64_mac, "Updating fdb entry from Ethernet64 failed" + + # remove FDB entry from Application DB + remove_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan6:00-00-00-00-00-11" + ) + + # restore the default value of the servers + dvs.servers[16].runcmd("ip route del default via 6.6.6.1") + dvs.servers[16].runcmd("ifconfig eth0 0") + dvs.servers[17].runcmd("ip route del default via 6.6.6.1") + dvs.servers[17].runcmd("ifconfig eth0 0") + + # bring down port + dvs.set_interface_status("Ethernet64", "down") + dvs.set_interface_status("Ethernet68", "down") + dvs.set_interface_status("Ethernet72", "down") + + # remove vlan ip + key = "Vlan6" + "|" + "6.6.6.1/24" + remove_entry_tbl(dvs.cdb, "VLAN_INTERFACE", key) + + # bring down vlan + dvs.set_interface_status("Vlan6", "down") + + # remove vlan member; remove vlan + dvs.remove_vlan_member("6", "Ethernet64") + dvs.remove_vlan_member("6", "Ethernet68") + dvs.remove_vlan_member("6", "Ethernet72") + dvs.remove_vlan("6") + + # clear fdb + dvs.runcmd("sonic-clear fdb all") diff --git a/tests/test_fdb_warm.py b/tests/test_fdb_warm.py deleted file mode 100644 index 0754e9628d6..00000000000 --- a/tests/test_fdb_warm.py +++ /dev/null @@ -1,318 +0,0 @@ -from swsscommon import swsscommon -import os -import sys -import time -import json -from distutils.version import StrictVersion - -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - - # FIXME: better to wait until DB create them - time.sleep(1) - -def create_entry_tbl(db, table, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def create_entry_pst(db, table, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - -def test_fdb_notifications(dvs, testlog): - dvs.setup_db() - - dvs.runcmd("sonic-clear fdb all") - - dvs.runcmd("crm config polling interval 1") - dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') - - time.sleep(2) - counter_before = dvs.getCrmCounterValue('STATS', 'crm_stats_fdb_entry_used') - - # create vlan; create vlan member - dvs.create_vlan("6") - dvs.create_vlan_member("6", "Ethernet64") - dvs.create_vlan_member("6", "Ethernet68") - - # Put Ethernet72 and Ethernet76 into vlan 7 in untagged mode, they will have pvid of 7 - # and into vlan8 in tagged mode - dvs.create_vlan("7") - dvs.create_vlan_member("7", "Ethernet72") - dvs.create_vlan_member("7", "Ethernet76") - - dvs.create_vlan("8") - dvs.create_vlan_member_tagged("8", "Ethernet72") - dvs.create_vlan_member_tagged("8", "Ethernet76") - - - # Get mapping between interface name and its bridge port_id - time.sleep(2) - iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) - - # check FDB learning mode - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet64"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet68"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet72"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet76"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - - # check fdb aging attr - ok, extra = dvs.all_table_entry_has_no(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH', - ".*", - ["SAI_SWITCH_ATTR_FDB_AGING_TIME"]) - - # bring up vlan and member - dvs.set_interface_status("Vlan6", "up") - dvs.set_interface_status("Vlan7", "up") - dvs.set_interface_status("Vlan8", "up") - - dvs.add_ip_address("Vlan6", "6.6.6.1/24") - dvs.add_ip_address("Vlan7", "7.7.7.1/24") - dvs.add_ip_address("Vlan8", "8.8.8.1/24") - - dvs.set_interface_status("Ethernet64", "up") - dvs.set_interface_status("Ethernet68", "up") - dvs.set_interface_status("Ethernet72", "up") - dvs.set_interface_status("Ethernet76", "up") - dvs.servers[16].runcmd("ifconfig eth0 6.6.6.6/24 up") - dvs.servers[16].runcmd("ip route add default via 6.6.6.1") - dvs.servers[17].runcmd("ifconfig eth0 6.6.6.7/24 up") - dvs.servers[17].runcmd("ip route add default via 6.6.6.1") - - dvs.servers[18].runcmd("vconfig add eth0 8") - dvs.servers[18].runcmd("ifconfig eth0.8 8.8.8.6/24 up") - dvs.servers[18].runcmd("ip route add default via 8.8.8.1") - - dvs.servers[19].runcmd("vconfig add eth0 8") - dvs.servers[19].runcmd("ifconfig eth0.8 8.8.8.7/24 up") - dvs.servers[19].runcmd("ip route add default via 8.8.8.1") - - dvs.servers[18].runcmd("ifconfig eth0 7.7.7.6/24 up") - dvs.servers[18].runcmd("ip route add default via 7.7.7.1") - dvs.servers[19].runcmd("ifconfig eth0 7.7.7.7/24 up") - dvs.servers[19].runcmd("ip route add default via 7.7.7.1") - - # get neighbor and arp entry - time.sleep(2) - rc = dvs.servers[16].runcmd("ping -c 1 6.6.6.7") - assert rc == 0 - rc = dvs.servers[17].runcmd("ping -c 1 6.6.6.6") - assert rc == 0 - - # get neighbor and arp entry - time.sleep(2) - rc = dvs.servers[18].runcmd("ping -c 1 8.8.8.7") - assert rc == 0 - rc = dvs.servers[19].runcmd("ping -c 1 8.8.8.6") - assert rc == 0 - - time.sleep(2) - rc = dvs.servers[18].runcmd("ping -c 1 -I 7.7.7.6 7.7.7.7") - assert rc == 0 - rc = dvs.servers[19].runcmd("ping -c 1 -I 7.7.7.7 7.7.7.6") - assert rc == 0 - - # check that the FDB entries were inserted into ASIC DB - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet64"]), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet68"]), - ] - ) - assert ok, str(extra) - - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet72"]), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet76"]), - ] - ) - assert ok, str(extra) - - time.sleep(2) - counter_inserted = dvs.getCrmCounterValue('STATS', 'crm_stats_fdb_entry_used') - # vlan 6: Ethernet64, Ethernet68; - # vlan 7: Ethernet72, Ethernet76; - # vlan 8 (tagged): Ethernet72, Ethernet76; - # 6 FDB entries wil be created in total - assert counter_inserted - counter_before == 6 - - # check that the FDB entries were inserted into State DB for Ethernet64, Ethernet68 with Vlan6 - ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", - "Vlan6:.*", - [("port", "Ethernet64"), - ("type", "dynamic"), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", - "Vlan6:*", - [("port", "Ethernet68"), - ("type", "dynamic"), - ] - ) - assert ok, str(extra) - - # check that the FDB entries were inserted into State DB, - # Vlan7(untagged) in the key for Ethernet72, Ethernet76 - ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", - "Vlan7:.*", - [("port", "Ethernet72"), - ("type", "dynamic"), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", - "Vlan7:*", - [("port", "Ethernet76"), - ("type", "dynamic"), - ] - ) - assert ok, str(extra) - - # check that the FDB entries were inserted into State DB, - # Vlan8 (tagged) in the key for Ethernet72, Ethernet76 - ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", - "Vlan8:.*", - [("port", "Ethernet72"), - ("type", "dynamic"), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", - "Vlan8:*", - [("port", "Ethernet76"), - ("type", "dynamic"), - ] - ) - assert ok, str(extra) - - # enable warm restart - (exitcode, result) = dvs.runcmd("config warm_restart enable swss") - assert exitcode == 0 - - # freeze orchagent for warm restart - (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") - assert result == "RESTARTCHECK succeeded\n" - time.sleep(2) - - try: - # restart orchagent - dvs.stop_swss() - - # check FDB learning mode - ok, extra = dvs.all_table_entry_has(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - ".*", - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE")]) - assert ok, str(extra) - # check FDB aging time - ok, extra = dvs.all_table_entry_has(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH', - ".*", - [("SAI_SWITCH_ATTR_FDB_AGING_TIME", "0")]) - assert ok, str(extra) - - dvs.start_swss() - time.sleep(2) - - # Get mapping between interface name and its bridge port_id - # Note: they are changed - iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) - - # check that the FDB entries were inserted into ASIC DB - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet64"]), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet68"]), - ] - ) - assert ok, str(extra) - - # check that the FDB entries were inserted into ASIC DB - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet72"]), - ] - ) - assert ok, str(extra) - ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", - [], - [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), - ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet76"]), - ] - ) - assert ok, str(extra) - - # check FDB learning mode - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet64"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet68"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet72"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - ok, extra = dvs.is_table_entry_exists(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT', - iface_2_bridge_port_id["Ethernet76"], - [("SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE", "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW")]) - assert ok, str(extra) - - time.sleep(2) - counter_restarted = dvs.getCrmCounterValue('STATS', 'crm_stats_fdb_entry_used') - assert counter_inserted == counter_restarted - - # check fdb aging attr - ok, extra = dvs.all_table_entry_has_no(dvs.adb, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH', - ".*", - ["SAI_SWITCH_ATTR_FDB_AGING_TIME"]) - - finally: - # disable warm restart - dvs.runcmd("config warm_restart disable swss") - # slow down crm polling - dvs.runcmd("crm config polling interval 10000") diff --git a/tests/test_mirror.py b/tests/test_mirror.py index bcb5b622365..80db2507557 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -140,7 +140,7 @@ def test_MirrorAddRemove(self, dvs, testlog): # add route to mirror destination via 10.0.0.1 self.add_route(dvs, "2.2.2.2", "10.0.0.1") assert self.get_mirror_session_state(session)["status"] == "active" - assert self.get_mirror_session_state(session)["monitor_port"] == dvs.asicdb.portnamemap["Ethernet16"] + assert self.get_mirror_session_state(session)["monitor_port"] == "Ethernet16" assert self.get_mirror_session_state(session)["dst_mac"] == "02:04:06:08:10:12" assert self.get_mirror_session_state(session)["route_prefix"] == "2.2.2.2/32" @@ -701,10 +701,18 @@ def remove_acl_table(self, table): tbl._del(table) time.sleep(1) - def create_mirror_acl_dscp_rule(self, table, rule, dscp, session): + def create_mirror_acl_dscp_rule(self, table, rule, dscp, session, stage=None): + action_name = "mirror_action" + action_name_map = { + "ingress": "MIRROR_INGRESS_ACTION", + "egress": "MIRROR_EGRESS_ACTION" + } + if stage is not None: # else it should be treated as ingress by default in orchagent + assert stage in ('ingress', 'egress'), "invalid stage input {}".format(stage) + action_name = action_name_map[stage] tbl = swsscommon.Table(self.cdb, "ACL_RULE") fvs = swsscommon.FieldValuePairs([("priority", "1000"), - ("mirror_action", session), + (action_name, session), ("DSCP", dscp)]) tbl.set(table + "|" + rule, fvs) time.sleep(1) @@ -714,6 +722,74 @@ def remove_mirror_acl_dscp_rule(self, table, rule): tbl._del(table + "|" + rule) time.sleep(1) + def test_AclBindMirrorPerStage(self, dvs, testlog): + """ + This test configures mirror rules with specifying explicitely + the mirror action stage (ingress, egress) and verifies ASIC db + entry set with correct mirror action + """ + self.setup_db(dvs) + + session = "MIRROR_SESSION" + acl_table = "MIRROR_TABLE" + acl_rule = "MIRROR_RULE" + + # bring up port; assign ip; create neighbor; create route + self.set_interface_status(dvs, "Ethernet32", "up") + self.add_ip_address("Ethernet32", "20.0.0.0/31") + self.add_neighbor("Ethernet32", "20.0.0.1", "02:04:06:08:10:12") + self.add_route(dvs, "4.4.4.4", "20.0.0.1") + + # create mirror session + self.create_mirror_session(session, "3.3.3.3", "4.4.4.4", "0x6558", "8", "100", "0") + assert self.get_mirror_session_state(session)["status"] == "active" + + # assert mirror session in asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + mirror_session_oid = tbl.getKeys()[0] + + # create acl table + self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"]) + + for stage, asic_attr in (("ingress", "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS"), + ("egress", "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_EGRESS")): + # create acl rule with dscp value 48 + self.create_mirror_acl_dscp_rule(acl_table, acl_rule, "48", session, stage=stage) + + # assert acl rule is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(rule_entries) == 1 + + (status, fvs) = tbl.get(rule_entries[0]) + assert status == True + + asic_attr_found = False + for fv in fvs: + if fv[0] == asic_attr: + asic_attr_found = True + + assert asic_attr_found == True + + # remove acl rule + self.remove_mirror_acl_dscp_rule(acl_table, acl_rule) + + # remove acl table + self.remove_acl_table(acl_table) + + # remove mirror session + self.remove_mirror_session(session) + + # assert no mirror session in asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 0 + + # remove route; remove neighbor; remove ip; bring down port + self.remove_route(dvs, "4.4.4.4") + self.remove_neighbor("Ethernet32", "20.0.0.1") + self.remove_ip_address("Ethernet32", "20.0.0.0/31") + self.set_interface_status(dvs, "Ethernet32", "down") def test_AclBindMirror(self, dvs, testlog): """ diff --git a/tests/test_nhg.py b/tests/test_nhg.py index 97a70d571ee..a2bbe5fd1f5 100644 --- a/tests/test_nhg.py +++ b/tests/test_nhg.py @@ -4,134 +4,134 @@ import time import json -def test_route_nhg(dvs, testlog): +class TestNextHopGroup(object): + def test_route_nhg(self, dvs, testlog): + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + intf_tbl.set("Ethernet8", fvs) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + intf_tbl.set("Ethernet8|10.0.0.4/31", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") + dvs.runcmd("ifconfig Ethernet8 up") + + dvs.runcmd("arp -s 10.0.0.1 00:00:00:00:00:01") + dvs.runcmd("arp -s 10.0.0.3 00:00:00:00:00:02") + dvs.runcmd("arp -s 10.0.0.5 00:00:00:00:00:03") + + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[1].runcmd("ip link set down dev eth0") == 0 + dvs.servers[2].runcmd("ip link set down dev eth0") == 0 + + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[1].runcmd("ip link set up dev eth0") == 0 + dvs.servers[2].runcmd("ip link set up dev eth0") == 0 + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3,10.0.0.5"), ("ifname", "Ethernet0,Ethernet4,Ethernet8")]) + + ps.set("2.2.2.0/24", fvs) - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - intf_tbl.set("Ethernet8", fvs) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - intf_tbl.set("Ethernet8|10.0.0.4/31", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") - dvs.runcmd("ifconfig Ethernet8 up") - - dvs.runcmd("arp -s 10.0.0.1 00:00:00:00:00:01") - dvs.runcmd("arp -s 10.0.0.3 00:00:00:00:00:02") - dvs.runcmd("arp -s 10.0.0.5 00:00:00:00:00:03") - - dvs.servers[0].runcmd("ip link set down dev eth0") == 0 - dvs.servers[1].runcmd("ip link set down dev eth0") == 0 - dvs.servers[2].runcmd("ip link set down dev eth0") == 0 - - dvs.servers[0].runcmd("ip link set up dev eth0") == 0 - dvs.servers[1].runcmd("ip link set up dev eth0") == 0 - dvs.servers[2].runcmd("ip link set up dev eth0") == 0 - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3,10.0.0.5"), ("ifname", "Ethernet0,Ethernet4,Ethernet8")]) - - ps.set("2.2.2.0/24", fvs) - - time.sleep(1) + time.sleep(1) - # check if route was propagated to ASIC DB + # check if route was propagated to ASIC DB - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - rtbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") - nhgtbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") - nhg_member_tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + rtbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + nhgtbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + nhg_member_tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") - keys = rtbl.getKeys() + keys = rtbl.getKeys() - found_route = False - for k in keys: - rt_key = json.loads(k) + found_route = False + for k in keys: + rt_key = json.loads(k) - if rt_key['dest'] == "2.2.2.0/24": - found_route = True - break + if rt_key['dest'] == "2.2.2.0/24": + found_route = True + break - assert found_route + assert found_route - # assert the route points to next hop group - (status, fvs) = rtbl.get(k) + # assert the route points to next hop group + (status, fvs) = rtbl.get(k) - for v in fvs: - if v[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": - nhgid = v[1] + for v in fvs: + if v[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + nhgid = v[1] - (status, fvs) = nhgtbl.get(nhgid) + (status, fvs) = nhgtbl.get(nhgid) - assert status + assert status - keys = nhg_member_tbl.getKeys() + keys = nhg_member_tbl.getKeys() - assert len(keys) == 3 + assert len(keys) == 3 - for k in keys: - (status, fvs) = nhg_member_tbl.get(k) + for k in keys: + (status, fvs) = nhg_member_tbl.get(k) - for v in fvs: - if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": - assert v[1] == nhgid + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert v[1] == nhgid - # bring links down one-by-one - for i in [0, 1, 2]: - dvs.servers[i].runcmd("ip link set down dev eth0") == 0 + # bring links down one-by-one + for i in [0, 1, 2]: + dvs.servers[i].runcmd("ip link set down dev eth0") == 0 - time.sleep(1) + time.sleep(1) - tbl = swsscommon.Table(db, "PORT_TABLE") - (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) + tbl = swsscommon.Table(db, "PORT_TABLE") + (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) - assert status == True + assert status == True - oper_status = "unknown" + oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break - assert oper_status == "down" + assert oper_status == "down" - keys = nhg_member_tbl.getKeys() + keys = nhg_member_tbl.getKeys() - assert len(keys) == 2 - i + assert len(keys) == 2 - i - # bring links up one-by-one - for i in [0, 1, 2]: - dvs.servers[i].runcmd("ip link set up dev eth0") == 0 + # bring links up one-by-one + for i in [0, 1, 2]: + dvs.servers[i].runcmd("ip link set up dev eth0") == 0 - time.sleep(1) + time.sleep(1) - tbl = swsscommon.Table(db, "PORT_TABLE") - (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) + tbl = swsscommon.Table(db, "PORT_TABLE") + (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) - assert status == True + assert status == True - oper_status = "unknown" + oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break - assert oper_status == "up" + assert oper_status == "up" - keys = nhg_member_tbl.getKeys() + keys = nhg_member_tbl.getKeys() - assert len(keys) == i + 1 + assert len(keys) == i + 1 - for k in keys: - (status, fvs) = nhg_member_tbl.get(k) + for k in keys: + (status, fvs) = nhg_member_tbl.get(k) - for v in fvs: - if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": - assert v[1] == nhgid + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert v[1] == nhgid diff --git a/tests/test_pfc.py b/tests/test_pfc.py index 8ed9112beab..9a59adf20b5 100644 --- a/tests/test_pfc.py +++ b/tests/test_pfc.py @@ -57,45 +57,45 @@ def getPortAttr(dvs, port_oid, port_attr): return '' +class TestPfc(object): + def test_PfcAsymmetric(self, dvs, testlog): -def test_PfcAsymmetric(dvs, testlog): + port_name = 'Ethernet0' + pfc_queues = [ 3, 4 ] - port_name = 'Ethernet0' - pfc_queues = [ 3, 4 ] + # Configure default PFC + setPortPfc(dvs, port_name, pfc_queues) - # Configure default PFC - setPortPfc(dvs, port_name, pfc_queues) + # Get SAI object ID for the interface + port_oid = getPortOid(dvs, port_name) - # Get SAI object ID for the interface - port_oid = getPortOid(dvs, port_name) + # Verify default PFC is set to configured value + pfc = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL') + assert pfc == getBitMaskStr(pfc_queues) - # Verify default PFC is set to configured value - pfc = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL') - assert pfc == getBitMaskStr(pfc_queues) + # Enable asymmetric PFC + setPortPfcAsym(dvs, port_name, 'on') - # Enable asymmetric PFC - setPortPfcAsym(dvs, port_name, 'on') + # Verify PFC mode is set to 'SEPARATE' + pfc_mode = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_MODE') + assert pfc_mode == 'SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE' - # Verify PFC mode is set to 'SEPARATE' - pfc_mode = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_MODE') - assert pfc_mode == 'SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_SEPARATE' + # Verify TX PFC is set to previous PFC value + pfc_tx = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_TX') + assert pfc_tx == pfc - # Verify TX PFC is set to previous PFC value - pfc_tx = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_TX') - assert pfc_tx == pfc + # Verify RX PFC is set to 0xFF (255) + pfc_rx = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_RX') + assert pfc_rx == '255' - # Verify RX PFC is set to 0xFF (255) - pfc_rx = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_RX') - assert pfc_rx == '255' + # Disable asymmetric PFC + setPortPfcAsym(dvs, port_name, 'off') - # Disable asymmetric PFC - setPortPfcAsym(dvs, port_name, 'off') + # Verify PFC mode is set to 'COMBINED' + pfc_mode = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_MODE') + assert pfc_mode == 'SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED' - # Verify PFC mode is set to 'COMBINED' - pfc_mode = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_MODE') - assert pfc_mode == 'SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED' - - # Verify PFC is set to TX PFC value - pfc = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL') - assert pfc == pfc_tx + # Verify PFC is set to TX PFC value + pfc = getPortAttr(dvs, port_oid, 'SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL') + assert pfc == pfc_tx diff --git a/tests/test_policer.py b/tests/test_policer.py new file mode 100644 index 00000000000..4ab455869f6 --- /dev/null +++ b/tests/test_policer.py @@ -0,0 +1,73 @@ +import platform +import pytest +import time + +from swsscommon import swsscommon + +class TestPolicer(object): + def test_PolicerBasic(self, dvs, testlog): + dvs.setup_db() + policer = "POLICER" + + # create policer + tbl = swsscommon.Table(dvs.cdb, "POLICER") + fvs = swsscommon.FieldValuePairs([("meter_type", "packets"), + ("mode", "sr_tcm"), + ("cir", "600"), + ("cbs", "600"), + ("red_packet_action", "drop")]) + tbl.set(policer, fvs) + time.sleep(1) + + # check asic database + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + policer_entries = tbl.getKeys() + assert len(policer_entries) == 1 + + (status, fvs) = tbl.get(policer_entries[0]) + assert status == True + assert len(fvs) == 5 + for fv in fvs: + if fv[0] == "SAI_POLICER_ATTR_CBS": + assert fv[1] == "600" + if fv[0] == "SAI_POLICER_ATTR_CIR": + assert fv[1] == "600" + if fv[0] == "SAI_POLICER_ATTR_RED_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + if fv[0] == "SAI_POLICER_ATTR_MODE": + assert fv[1] == "SAI_POLICER_MODE_SR_TCM" + if fv[0] == "SAI_POLICER_ATTR_METER_TYPE": + assert fv[1] == "SAI_METER_TYPE_PACKETS" + + # update cir + tbl = swsscommon.Table(dvs.cdb, "POLICER") + fvs = swsscommon.FieldValuePairs([("cir", "800")]) + tbl.set(policer, fvs) + time.sleep(1) + + # check asic database + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + (status, fvs) = tbl.get(policer_entries[0]) + assert status == True + assert len(fvs) == 5 + for fv in fvs: + if fv[0] == "SAI_POLICER_ATTR_CBS": + assert fv[1] == "600" + if fv[0] == "SAI_POLICER_ATTR_CIR": + assert fv[1] == "800" # updated + if fv[0] == "SAI_POLICER_ATTR_RED_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + if fv[0] == "SAI_POLICER_ATTR_MODE": + assert fv[1] == "SAI_POLICER_MODE_SR_TCM" + if fv[0] == "SAI_POLICER_ATTR_METER_TYPE": + assert fv[1] == "SAI_METER_TYPE_PACKETS" + + # remove policer + tbl = swsscommon.Table(dvs.cdb, "POLICER") + tbl._del(policer) + time.sleep(1) + + # check asic database + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + policer_entries = tbl.getKeys() + assert len(policer_entries) == 0 diff --git a/tests/test_port.py b/tests/test_port.py index cf429ca985a..edf459301c7 100644 --- a/tests/test_port.py +++ b/tests/test_port.py @@ -23,195 +23,195 @@ def test_PortMtu(self, dvs, testlog): if fv[0] == "mtu": assert fv[1] == "9100" -def test_PortNotification(dvs, testlog): + def test_PortNotification(self, dvs, testlog): - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") == 0 - dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") == 0 + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") == 0 + dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") == 0 - dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 - time.sleep(1) + time.sleep(1) - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "PORT_TABLE") + tbl = swsscommon.Table(db, "PORT_TABLE") - (status, fvs) = tbl.get("Ethernet0") + (status, fvs) = tbl.get("Ethernet0") - assert status == True + assert status == True - oper_status = "unknown" + oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break - assert oper_status == "down" + assert oper_status == "down" - dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 - time.sleep(1) + time.sleep(1) - (status, fvs) = tbl.get("Ethernet0") + (status, fvs) = tbl.get("Ethernet0") - assert status == True + assert status == True - oper_status = "unknown" + oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break - assert oper_status == "up" + assert oper_status == "up" -def test_PortFec(dvs, testlog): + def test_PortFec(self, dvs, testlog): - dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") == 0 - dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") == 0 + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") == 0 + dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") == 0 - dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 - time.sleep(1) + time.sleep(1) - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "PORT_TABLE") - ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + tbl = swsscommon.Table(db, "PORT_TABLE") + ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - (status, fvs) = tbl.get("Ethernet0") + (status, fvs) = tbl.get("Ethernet0") - assert status == True + assert status == True - oper_status = "unknown" + oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break - assert oper_status == "down" + assert oper_status == "down" - dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 - time.sleep(1) + time.sleep(1) - (status, fvs) = tbl.get("Ethernet0") + (status, fvs) = tbl.get("Ethernet0") - assert status == True + assert status == True - oper_status = "unknown" + oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break - assert oper_status == "up" + assert oper_status == "up" - # set fec - fvs = swsscommon.FieldValuePairs([("fec","rs"), ("speed", "1000")]) - ptbl.set("Ethernet0", fvs) + # set fec + fvs = swsscommon.FieldValuePairs([("fec","rs"), ("speed", "1000")]) + ptbl.set("Ethernet0", fvs) - time.sleep(1) + time.sleep(1) - # get fec - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True + # get fec + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_FEC_MODE": - assert fv[1] == "SAI_PORT_FEC_MODE_RS" + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_FEC_MODE": + assert fv[1] == "SAI_PORT_FEC_MODE_RS" -def test_PortPreemp(dvs, testlog): + def test_PortPreemp(self, dvs, testlog): - pre_name = 'preemphasis' - pre_val = [0x1234,0x2345,0x3456,0x4567] - pre_val_str = str(hex(pre_val[0])) + "," + str(hex(pre_val[1]))+ "," + \ - str(hex(pre_val[2]))+ "," + str(hex(pre_val[3])) + pre_name = 'preemphasis' + pre_val = [0x1234,0x2345,0x3456,0x4567] + pre_val_str = str(hex(pre_val[0])) + "," + str(hex(pre_val[1]))+ "," + \ + str(hex(pre_val[2]))+ "," + str(hex(pre_val[3])) - pre_val_asic = '4:' + str(pre_val[0]) + "," + str(pre_val[1]) + "," + \ - str(pre_val[2]) + "," + str(pre_val[3]) - fvs = swsscommon.FieldValuePairs([(pre_name, pre_val_str)]) - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + pre_val_asic = '4:' + str(pre_val[0]) + "," + str(pre_val[1]) + "," + \ + str(pre_val[2]) + "," + str(pre_val[3]) + fvs = swsscommon.FieldValuePairs([(pre_name, pre_val_str)]) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "PORT_TABLE") - ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + tbl = swsscommon.Table(db, "PORT_TABLE") + ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - ptbl.set("Ethernet0", fvs) + ptbl.set("Ethernet0", fvs) - time.sleep(1) + time.sleep(1) - # get fec - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True + # get fec + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_SERDES_PREEMPHASIS": - assert fv[1] == pre_val_asic + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_SERDES_PREEMPHASIS": + assert fv[1] == pre_val_asic -def test_PortIdriver(dvs, testlog): + def test_PortIdriver(self, dvs, testlog): - idrv_name = 'idriver' - idrv_val = [0x1,0x1,0x2,0x2] - idrv_val_str = str(hex(idrv_val[0])) + "," + str(hex(idrv_val[1]))+ "," + \ - str(hex(idrv_val[2]))+ "," + str(hex(idrv_val[3])) + idrv_name = 'idriver' + idrv_val = [0x1,0x1,0x2,0x2] + idrv_val_str = str(hex(idrv_val[0])) + "," + str(hex(idrv_val[1]))+ "," + \ + str(hex(idrv_val[2]))+ "," + str(hex(idrv_val[3])) - idrv_val_asic = '4:' + str(idrv_val[0]) + "," + str(idrv_val[1]) + "," + \ - str(idrv_val[2]) + "," + str(idrv_val[3]) - fvs = swsscommon.FieldValuePairs([(idrv_name, idrv_val_str)]) - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + idrv_val_asic = '4:' + str(idrv_val[0]) + "," + str(idrv_val[1]) + "," + \ + str(idrv_val[2]) + "," + str(idrv_val[3]) + fvs = swsscommon.FieldValuePairs([(idrv_name, idrv_val_str)]) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "PORT_TABLE") - ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + tbl = swsscommon.Table(db, "PORT_TABLE") + ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - ptbl.set("Ethernet0", fvs) + ptbl.set("Ethernet0", fvs) - time.sleep(1) + time.sleep(1) - # get fec - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True + # get fec + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_SERDES_IDRIVER": - assert fv[1] == idrv_val_asic + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_SERDES_IDRIVER": + assert fv[1] == idrv_val_asic -def test_PortIpredriver(dvs, testlog): + def test_PortIpredriver(self, dvs, testlog): - ipre_name = 'ipredriver' - ipre_val = [0x2,0x3,0x4,0x5] - ipre_val_str = str(hex(ipre_val[0])) + "," + str(hex(ipre_val[1]))+ "," + \ - str(hex(ipre_val[2]))+ "," + str(hex(ipre_val[3])) + ipre_name = 'ipredriver' + ipre_val = [0x2,0x3,0x4,0x5] + ipre_val_str = str(hex(ipre_val[0])) + "," + str(hex(ipre_val[1]))+ "," + \ + str(hex(ipre_val[2]))+ "," + str(hex(ipre_val[3])) - ipre_val_asic = '4:' + str(ipre_val[0]) + "," + str(ipre_val[1]) + "," + \ - str(ipre_val[2]) + "," + str(ipre_val[3]) - fvs = swsscommon.FieldValuePairs([(ipre_name, ipre_val_str)]) - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + ipre_val_asic = '4:' + str(ipre_val[0]) + "," + str(ipre_val[1]) + "," + \ + str(ipre_val[2]) + "," + str(ipre_val[3]) + fvs = swsscommon.FieldValuePairs([(ipre_name, ipre_val_str)]) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "PORT_TABLE") - ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + tbl = swsscommon.Table(db, "PORT_TABLE") + ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - ptbl.set("Ethernet0", fvs) + ptbl.set("Ethernet0", fvs) - time.sleep(1) + time.sleep(1) - # get fec - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True + # get fec + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_SERDES_IPREDRIVER": - assert fv[1] == ipre_val_asic + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_SERDES_IPREDRIVER": + assert fv[1] == ipre_val_asic diff --git a/tests/test_port_an.py b/tests/test_port_an.py new file mode 100644 index 00000000000..67ce59f317a --- /dev/null +++ b/tests/test_port_an.py @@ -0,0 +1,173 @@ +from swsscommon import swsscommon +import time +import os + +class TestPortAutoNeg(object): + def test_PortAutoNegCold(self, dvs, testlog): + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + + # set autoneg = false and speed = 1000 + fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:1000" + + # set speed = 100 + fvs = swsscommon.FieldValuePairs([("speed", "100")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + + # change autoneg to false + fvs = swsscommon.FieldValuePairs([("autoneg","0")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_SPEED" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "false" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + elif fv[0] == "SAI_PORT_ATTR_SPEED": + assert fv[1] == "100" + + # set speed = 1000 + fvs = swsscommon.FieldValuePairs([("speed", "1000")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "false" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + elif fv[0] == "SAI_PORT_ATTR_SPEED": + assert fv[1] == "1000" + + def test_PortAutoNegWarm(self, dvs, testlog): + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + ctbl = swsscommon.Table(cdb, "PORT") + stbl = swsscommon.Table(sdb, "PORT_TABLE") + + # set autoneg = true and speed = 1000 + fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) + ctbl.set("Ethernet0", fvs) + + time.sleep(1) + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:1000" + + # set speed = 100 + fvs = swsscommon.FieldValuePairs([("speed", "100")]) + + ctbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + + # set admin up + cfvs = swsscommon.FieldValuePairs([("admin_status", "up")]) + ctbl.set("Ethernet0", cfvs) + + # enable warm restart + (exitcode, result) = dvs.runcmd("config warm_restart enable swss") + assert exitcode == 0 + + # freeze orchagent for warm restart + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK succeeded\n" + time.sleep(2) + + try: + # restart orchagent + # clean port state + dvs.stop_swss() + ports = stbl.getKeys() + for port in ports: + stbl._del(port) + dvs.start_swss() + time.sleep(2) + + # check ASIC DB after warm restart + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + + finally: + # disable warm restart + dvs.runcmd("config warm_restart disable swss") + # slow down crm polling + dvs.runcmd("crm config polling interval 10000") diff --git a/tests/test_port_an_cold.py b/tests/test_port_an_cold.py deleted file mode 100644 index 3b4414059a3..00000000000 --- a/tests/test_port_an_cold.py +++ /dev/null @@ -1,85 +0,0 @@ -from swsscommon import swsscommon -import time -import os - -def test_PortAutoNeg(dvs, testlog): - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - - tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - - # set autoneg = false and speed = 1000 - fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) - - tbl.set("Ethernet0", fvs) - - time.sleep(1) - - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] - assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "true" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:1000" - - # set speed = 100 - fvs = swsscommon.FieldValuePairs([("speed", "100")]) - - tbl.set("Ethernet0", fvs) - - time.sleep(1) - - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "true" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:100" - - # change autoneg to false - fvs = swsscommon.FieldValuePairs([("autoneg","0")]) - - tbl.set("Ethernet0", fvs) - - time.sleep(1) - - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] - assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] - assert "SAI_PORT_ATTR_SPEED" in [fv[0] for fv in fvs] - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "false" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:100" - elif fv[0] == "SAI_PORT_ATTR_SPEED": - assert fv[1] == "100" - - # set speed = 1000 - fvs = swsscommon.FieldValuePairs([("speed", "1000")]) - - tbl.set("Ethernet0", fvs) - - time.sleep(1) - - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "false" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:100" - elif fv[0] == "SAI_PORT_ATTR_SPEED": - assert fv[1] == "1000" diff --git a/tests/test_port_an_warm.py b/tests/test_port_an_warm.py deleted file mode 100644 index 50ffbbf8192..00000000000 --- a/tests/test_port_an_warm.py +++ /dev/null @@ -1,90 +0,0 @@ -from swsscommon import swsscommon -import time -import os - -def test_PortAutoNeg_warm(dvs, testlog): - - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) - sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) - - tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - ctbl = swsscommon.Table(cdb, "PORT") - stbl = swsscommon.Table(sdb, "PORT_TABLE") - - # set autoneg = true and speed = 1000 - fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) - ctbl.set("Ethernet0", fvs) - - time.sleep(1) - - adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - - atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] - assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "true" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:1000" - - # set speed = 100 - fvs = swsscommon.FieldValuePairs([("speed", "100")]) - - ctbl.set("Ethernet0", fvs) - - time.sleep(1) - - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "true" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:100" - - # set admin up - cfvs = swsscommon.FieldValuePairs([("admin_status", "up")]) - ctbl.set("Ethernet0", cfvs) - - # enable warm restart - (exitcode, result) = dvs.runcmd("config warm_restart enable swss") - assert exitcode == 0 - - # freeze orchagent for warm restart - (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") - assert result == "RESTARTCHECK succeeded\n" - time.sleep(2) - - try: - # restart orchagent - # clean port state - dvs.stop_swss() - ports = stbl.getKeys() - for port in ports: - stbl._del(port) - dvs.start_swss() - time.sleep(2) - - # check ASIC DB after warm restart - (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) - assert status == True - - assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] - assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": - assert fv[1] == "true" - elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": - assert fv[1] == "1:100" - - finally: - # disable warm restart - dvs.runcmd("config warm_restart disable swss") - # slow down crm polling - dvs.runcmd("crm config polling interval 10000") diff --git a/tests/test_port_buffer_rel.py b/tests/test_port_buffer_rel.py index 231bec6ddf3..ed21660562a 100644 --- a/tests/test_port_buffer_rel.py +++ b/tests/test_port_buffer_rel.py @@ -2,32 +2,32 @@ import time # The test check that the ports will be up, when the admin state is UP by conf db. +class TestPortBuffer(object): + def test_PortsAreUpAfterBuffers(self, dvs, testlog): + num_ports = 32 + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) -def test_PortsAreUpAfterBuffers(dvs, testlog): - num_ports = 32 - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + conf_port_table = swsscommon.Table(conf_db, "PORT") + asic_port_table = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - conf_port_table = swsscommon.Table(conf_db, "PORT") - asic_port_table = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + # enable all ports + fvs = swsscommon.FieldValuePairs([("admin_status", "up")]) + for i in range(0, num_ports): + conf_port_table.set("Ethernet%d" % (i*4), fvs) - # enable all ports - fvs = swsscommon.FieldValuePairs([("admin_status", "up")]) - for i in range(0, num_ports): - conf_port_table.set("Ethernet%d" % (i*4), fvs) + time.sleep(5) - time.sleep(5) - - # check that the ports are enabled in ASIC - asic_port_records = asic_port_table.getKeys() - assert len(asic_port_records) == (num_ports + 1), "Number of port records doesn't match number of ports" # +CPU port - num_set = 0 - for k in asic_port_records: - status, fvs = asic_port_table.get(k) - assert status, "Got an error when get a key" - for fv in fvs: - if fv[0] == "SAI_PORT_ATTR_ADMIN_STATE": - assert fv[1] == "true", "The port isn't UP as expected" - num_set += 1 - # make sure that state is set for all "num_ports" ports - assert num_set == num_ports, "Not all ports are up" + # check that the ports are enabled in ASIC + asic_port_records = asic_port_table.getKeys() + assert len(asic_port_records) == (num_ports + 1), "Number of port records doesn't match number of ports" # +CPU port + num_set = 0 + for k in asic_port_records: + status, fvs = asic_port_table.get(k) + assert status, "Got an error when get a key" + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_ADMIN_STATE": + assert fv[1] == "true", "The port isn't UP as expected" + num_set += 1 + # make sure that state is set for all "num_ports" ports + assert num_set == num_ports, "Not all ports are up" diff --git a/tests/test_port_config.py b/tests/test_port_config.py new file mode 100644 index 00000000000..656b96565e1 --- /dev/null +++ b/tests/test_port_config.py @@ -0,0 +1,107 @@ +from swsscommon import swsscommon +import redis +import time +import os +import pytest + +@pytest.yield_fixture +def port_config(request, dvs): + file_name = "/usr/share/sonic/hwsku/port_config.ini" + dvs.runcmd("cp %s %s.bak" % (file_name, file_name)) + yield file_name + dvs.runcmd("mv %s.bak %s" % (file_name, file_name)) + +class TestPortConfig(object): + + def getPortName(self, dvs, port_vid): + cnt_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + port_map = swsscommon.Table(cnt_db, 'COUNTERS_PORT_NAME_MAP') + + for k in port_map.get('')[1]: + if k[1] == port_vid: + return k[0] + + return '' + + + def getPortOid(self, dvs, port_name): + cnt_r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.COUNTERS_DB) + return cnt_r.hget("COUNTERS_PORT_NAME_MAP", port_name); + + + def getVIDfromRID(self, dvs, port_rid): + asic_r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) + return asic_r.hget("RIDTOVID", port_rid); + + + def test_port_hw_lane(self, dvs): + + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + port_tbl = swsscommon.Table(app_db, "PORT_TABLE") + + asic_r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) + num_lanes = asic_r.hlen("LANES") + for i in range(1, num_lanes+1): + port_rid = asic_r.hget("LANES", i) + port_vid = self.getVIDfromRID(dvs, port_rid) + port_name = self.getPortName(dvs, port_vid) + + (status, fvs) = port_tbl.get(port_name) + assert status == True + for fv in fvs: + if fv[0] == "lanes": + assert str(i) in list(fv[1].split(",")) + + + def test_port_breakout(self, dvs, port_config): + + # check port_config.ini + (exitcode, output) = dvs.runcmd(['sh', '-c', "cat %s | tail -n 1" % (port_config)]) + try: + name_str, lanes_str, alias_str, index_str, speed_str = list(output.split()) + except: + print "parse port_config.ini fail" + + LANES_L = list(lanes_str.split(",")) + assert len(LANES_L) == 4 + assert int(speed_str) == 40000 + + # modify port_config.ini + eth = int(name_str.replace("Ethernet", "")) + index = int(index_str) + speed_str = "tenGigE0" + speed = 10000 + dvs.runcmd("sed -i '$d' %s" % (port_config)) == 0 + for i in range(0,4): + dvs.runcmd("sed -i '$a Ethernet%-7d %-17d %s/%-8d %-11d %d' %s" % + (eth+i, int(LANES_L[i]), speed_str, eth+i, index+i, speed, port_config)) == 0 + + # delete port config + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + portTbl = swsscommon.Table(conf_db, swsscommon.CFG_PORT_TABLE_NAME) + keys = portTbl.getKeys() + assert len(keys) > 0 + for key in keys: + portTbl._del(key) + + # restart to apply new port_config.ini + dvs.stop_swss() + dvs.start_swss() + time.sleep(5) + + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + for i in range(0,4): + port_name = 'Ethernet{0}'.format(eth+i) + port_oid = self.getPortOid(dvs, port_name) + port_tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_PORT:{0}'.format(port_oid)) + hw_lane_value = None + + for k in port_tbl.get('')[1]: + if k[0] == "SAI_PORT_ATTR_HW_LANE_LIST": + hw_lane_value = k[1] + + assert hw_lane_value, "Can't get hw_lane list" + assert hw_lane_value == "1:%s" % (LANES_L[i]) + + diff --git a/tests/test_portchannel.py b/tests/test_portchannel.py index e4875160f99..0bc6db1e2ce 100644 --- a/tests/test_portchannel.py +++ b/tests/test_portchannel.py @@ -3,56 +3,232 @@ import re import json -def test_PortChannel(dvs, testlog): +class TestPortchannel(object): + def test_Portchannel(self, dvs, testlog): - # create port channel - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "LAG_TABLE") - fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + # create port channel + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) - ps.set("PortChannel0001", fvs) + ps.set("PortChannel0001", fvs) - # create port channel member - ps = swsscommon.ProducerStateTable(db, "LAG_MEMBER_TABLE") - fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + # create port channel member + ps = swsscommon.ProducerStateTable(db, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) - ps.set("PortChannel0001:Ethernet0", fvs) + ps.set("PortChannel0001:Ethernet0", fvs) - time.sleep(1) + time.sleep(1) - # check asic db - asicdb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + # check asic db + asicdb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - lagtbl = swsscommon.Table(asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") - lags = lagtbl.getKeys() - assert len(lags) == 1 + lagtbl = swsscommon.Table(asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lags = lagtbl.getKeys() + assert len(lags) == 1 - lagmtbl = swsscommon.Table(asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER") - lagms = lagmtbl.getKeys() - assert len(lagms) == 1 + lagmtbl = swsscommon.Table(asicdb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER") + lagms = lagmtbl.getKeys() + assert len(lagms) == 1 - (status, fvs) = lagmtbl.get(lagms[0]) - for fv in fvs: - if fv[0] == "SAI_LAG_MEMBER_ATTR_LAG_ID": - assert fv[1] == lags[0] - elif fv[0] == "SAI_LAG_MEMBER_ATTR_PORT_ID": - assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet0" - else: - assert False + (status, fvs) = lagmtbl.get(lagms[0]) + for fv in fvs: + if fv[0] == "SAI_LAG_MEMBER_ATTR_LAG_ID": + assert fv[1] == lags[0] + elif fv[0] == "SAI_LAG_MEMBER_ATTR_PORT_ID": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet0" + else: + assert False - # remove port channel member - ps = swsscommon.ProducerStateTable(db, "LAG_MEMBER_TABLE") - ps._del("PortChannel0001:Ethernet0") + # remove port channel member + ps = swsscommon.ProducerStateTable(db, "LAG_MEMBER_TABLE") + ps._del("PortChannel0001:Ethernet0") - # remove port channel - ps = swsscommon.ProducerStateTable(db, "LAG_TABLE") - ps._del("PortChannel0001") + # remove port channel + ps = swsscommon.ProducerStateTable(db, "LAG_TABLE") + ps._del("PortChannel0001") - time.sleep(1) + time.sleep(1) - # check asic db - lags = lagtbl.getKeys() - assert len(lags) == 0 + # check asic db + lags = lagtbl.getKeys() + assert len(lags) == 0 - lagms = lagmtbl.getKeys() - assert len(lagms) == 0 + lagms = lagmtbl.getKeys() + assert len(lagms) == 0 + + + def test_Portchannel_oper_down(self, dvs, testlog): + + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + # Create 4 PortChannels + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"),("mtu", "9100"),("oper_status", "up")]) + + tbl.set("PortChannel001", fvs) + time.sleep(1) + tbl.set("PortChannel002", fvs) + time.sleep(1) + tbl.set("PortChannel003", fvs) + time.sleep(1) + tbl.set("PortChannel004", fvs) + time.sleep(1) + + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_MEMBER") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set("PortChannel001|Ethernet0", fvs) + time.sleep(1) + tbl.set("PortChannel002|Ethernet4", fvs) + time.sleep(1) + tbl.set("PortChannel003|Ethernet8", fvs) + time.sleep(1) + tbl.set("PortChannel004|Ethernet12", fvs) + time.sleep(1) + + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set("PortChannel001", fvs) + tbl.set("PortChannel001|40.0.0.0/31", fvs) + time.sleep(1) + tbl.set("PortChannel002", fvs) + tbl.set("PortChannel002|40.0.0.2/31", fvs) + time.sleep(1) + tbl.set("PortChannel003", fvs) + tbl.set("PortChannel003|40.0.0.4/31", fvs) + time.sleep(1) + tbl.set("PortChannel004", fvs) + tbl.set("PortChannel004|40.0.0.6/31", fvs) + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "40.0.0.0/31" + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel002") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "40.0.0.2/31" + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel003") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "40.0.0.4/31" + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel004") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "40.0.0.6/31" + + + # set oper_status for PortChannels + ps = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"),("mtu", "9100"),("oper_status", "up")]) + ps.set("PortChannel001", fvs) + ps.set("PortChannel002", fvs) + ps.set("PortChannel003", fvs) + ps.set("PortChannel004", fvs) + time.sleep(1) + + dvs.runcmd("arp -s 40.0.0.1 00:00:00:00:00:01") + time.sleep(1) + dvs.runcmd("arp -s 40.0.0.3 00:00:00:00:00:03") + time.sleep(1) + dvs.runcmd("arp -s 40.0.0.5 00:00:00:00:00:05") + time.sleep(1) + dvs.runcmd("arp -s 40.0.0.7 00:00:00:00:00:07") + time.sleep(1) + + ps = swsscommon.ProducerStateTable(self.pdb, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","40.0.0.1,40.0.0.3,40.0.0.5,40.0.0.7"), ("ifname", "PortChannel001,PortChannel002,PortChannel003,PortChannel004")]) + + ps.set("2.2.2.0/24", fvs) + time.sleep(1) + + # check if route has propagated to ASIC DB + re_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + + found_route = False + for key in re_tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "2.2.2.0/24": + found_route = True + break + + assert found_route + + # check if route points to next hop group + nhg_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + (status, fvs) = re_tbl.get(key) + for v in fvs: + if v[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + nhg_id = v[1] + + (status, fvs) = nhg_tbl.get(nhg_id) + assert status + + # check if next hop group consists of 4 members + nhg_member_tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + keys = nhg_member_tbl.getKeys() + assert len(keys) == 4 + + for key in keys: + (status, fvs) = nhg_member_tbl.get(key) + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert v[1] == nhg_id + + # bring PortChannel down + dvs.servers[0].runcmd("ip link set down dev eth0") + time.sleep(1) + ps = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"),("mtu", "9100"),("oper_status", "down")]) + ps.set("PortChannel001", fvs) + time.sleep(1) + + # check if next hop group consists of 3 member + keys = nhg_member_tbl.getKeys() + assert len(keys) == 3 + + # remove IP address + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_INTERFACE") + tbl._del("PortChannel001|40.0.0.0/31") + tbl._del("PortChannel002|40.0.0.2/31") + tbl._del("PortChannel003|40.0.0.4/31") + tbl._del("PortChannel004|40.0.0.6/31") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel002") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel003") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel004") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # remove PortChannel members + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL_MEMBER") + tbl._del("PortChannel001|Ethernet0") + tbl._del("PortChannel002|Ethernet4") + tbl._del("PortChannel003|Ethernet8") + tbl._del("PortChannel004|Ethernet12") + time.sleep(1) + + # remove PortChannel + tbl = swsscommon.Table(self.cdb, "PORTCHANNEL") + tbl._del("PortChannel001") + tbl._del("PortChannel002") + tbl._del("PortChannel003") + tbl._del("PortChannel004") + time.sleep(1) diff --git a/tests/test_qos_map.py b/tests/test_qos_map.py new file mode 100644 index 00000000000..03ff0c63e3c --- /dev/null +++ b/tests/test_qos_map.py @@ -0,0 +1,105 @@ +import pytest +import json +import sys +import time +from swsscommon import swsscommon + +CFG_DOT1P_TO_TC_MAP_TABLE_NAME = "DOT1P_TO_TC_MAP" +CFG_DOT1P_TO_TC_MAP_KEY = "AZURE" +DOT1P_TO_TC_MAP = { + "0": "0", + "1": "6", + "2": "5", + "3": "3", + "4": "4", + "5": "2", + "6": "1", + "7": "7", +} + +CFG_PORT_QOS_MAP_TABLE_NAME = "PORT_QOS_MAP" +CFG_PORT_QOS_MAP_FIELD = "dot1p_to_tc_map" +CFG_PORT_TABLE_NAME = "PORT" + + +class TestDot1p(object): + def connect_dbs(self, dvs): + self.asic_db = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.config_db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + + def create_dot1p_profile(self): + tbl = swsscommon.Table(self.config_db, CFG_DOT1P_TO_TC_MAP_TABLE_NAME) + fvs = swsscommon.FieldValuePairs(DOT1P_TO_TC_MAP.items()) + tbl.set(CFG_DOT1P_TO_TC_MAP_KEY, fvs) + time.sleep(1) + + + def find_dot1p_profile(self): + found = False + dot1p_tc_map_raw = None + dot1p_tc_map_key = None + tbl = swsscommon.Table(self.asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_QOS_MAP") + keys = tbl.getKeys() + for key in keys: + (status, fvs) = tbl.get(key) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST": + dot1p_tc_map_raw = fv[1] + elif fv[0] == "SAI_QOS_MAP_ATTR_TYPE" and fv[1] == "SAI_QOS_MAP_TYPE_DOT1P_TO_TC": + dot1p_tc_map_key = key + found = True + + if found: + break + + assert found == True + + return (key, dot1p_tc_map_raw) + + + def apply_dot1p_profile_on_all_ports(self): + tbl = swsscommon.Table(self.config_db, CFG_PORT_QOS_MAP_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([(CFG_PORT_QOS_MAP_FIELD, "[" + CFG_DOT1P_TO_TC_MAP_TABLE_NAME + "|" + CFG_DOT1P_TO_TC_MAP_KEY + "]")]) + ports = swsscommon.Table(self.config_db, CFG_PORT_TABLE_NAME).getKeys() + for port in ports: + tbl.set(port, fvs) + + time.sleep(1) + + + def test_dot1p_cfg(self, dvs): + self.connect_dbs(dvs) + self.create_dot1p_profile() + oid, dot1p_tc_map_raw = self.find_dot1p_profile() + + dot1p_tc_map = json.loads(dot1p_tc_map_raw); + for dot1p2tc in dot1p_tc_map['list']: + dot1p = str(dot1p2tc['key']['dot1p']) + tc = str(dot1p2tc['value']['tc']) + assert tc == DOT1P_TO_TC_MAP[dot1p] + + + def test_port_dot1p(self, dvs): + self.connect_dbs(dvs) + self.create_dot1p_profile() + oid, dot1p_tc_map_raw = self.find_dot1p_profile() + + self.apply_dot1p_profile_on_all_ports() + + cnt = 0 + tbl = swsscommon.Table(self.asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + keys = tbl.getKeys() + for key in keys: + (status, fvs) = tbl.get(key) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_QOS_DOT1P_TO_TC_MAP": + cnt += 1 + assert fv[1] == oid + + port_cnt = len(swsscommon.Table(self.config_db, CFG_PORT_TABLE_NAME).getKeys()) + assert port_cnt == cnt diff --git a/tests/test_route.py b/tests/test_route.py index d6f4a502c75..c9928214510 100644 --- a/tests/test_route.py +++ b/tests/test_route.py @@ -4,41 +4,42 @@ import time import json -def test_RouteAdd(dvs, testlog): +class TestRoute(object): + def test_RouteAdd(self, dvs, testlog): - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") - dvs.servers[0].runcmd("ifconfig eth0 10.0.0.1/31") - dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + dvs.servers[0].runcmd("ifconfig eth0 10.0.0.1/31") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") - dvs.servers[1].runcmd("ifconfig eth0 10.0.0.3/31") - dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + dvs.servers[1].runcmd("ifconfig eth0 10.0.0.3/31") + dvs.servers[1].runcmd("ip route add default via 10.0.0.2") - # get neighbor and arp entry - dvs.servers[0].runcmd("ping -c 1 10.0.0.3") + # get neighbor and arp entry + dvs.servers[0].runcmd("ping -c 1 10.0.0.3") - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) - pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_ROUTE_ENTRY") + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_ROUTE_ENTRY") - ps.set("2.2.2.0/24", fvs) + ps.set("2.2.2.0/24", fvs) - # check if route was propagated to ASIC DB + # check if route was propagated to ASIC DB - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsub) + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsub) - assert len(addobjs) == 1 + assert len(addobjs) == 1 - rt_key = json.loads(addobjs[0]['key']) + rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "2.2.2.0/24" + assert rt_key['dest'] == "2.2.2.0/24" diff --git a/tests/test_setro.py b/tests/test_setro.py index 8c962dd20e8..84b004a878b 100644 --- a/tests/test_setro.py +++ b/tests/test_setro.py @@ -4,37 +4,38 @@ import redis from pprint import pprint -def test_SetReadOnlyAttribute(dvs, testlog): +class TestSetRo(object): + def test_SetReadOnlyAttribute(self, dvs, testlog): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") - keys = tbl.getKeys() + keys = tbl.getKeys() - assert len(keys) == 1 + assert len(keys) == 1 - swVid = keys[0] - - r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) + swVid = keys[0] - swRid = r.hget("VIDTORID", swVid) + r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) - assert swRid is not None + swRid = r.hget("VIDTORID", swVid) - ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") + assert swRid is not None - fvp = swsscommon.FieldValuePairs() + ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") - ntf.send("enable_unittests", "true", fvp) + fvp = swsscommon.FieldValuePairs() - fvp = swsscommon.FieldValuePairs([('SAI_SWITCH_ATTR_PORT_MAX_MTU', '42')]) + ntf.send("enable_unittests", "true", fvp) - key = "SAI_OBJECT_TYPE_SWITCH:" + swRid + fvp = swsscommon.FieldValuePairs([('SAI_SWITCH_ATTR_PORT_MAX_MTU', '42')]) - print key + key = "SAI_OBJECT_TYPE_SWITCH:" + swRid - ntf.send("set_ro", key, fvp) + print key - # make action on appdb so orchagent will get RO value - # read asic db to see if orchagent behaved correctly + ntf.send("set_ro", key, fvp) + + # make action on appdb so orchagent will get RO value + # read asic db to see if orchagent behaved correctly diff --git a/tests/test_vlan.py b/tests/test_vlan.py index 0bc12af5490..4a0202dd624 100644 --- a/tests/test_vlan.py +++ b/tests/test_vlan.py @@ -12,6 +12,7 @@ def setup_db(self, dvs): self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + self.sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) def create_vlan(self, vlan): tbl = swsscommon.Table(self.cdb, "VLAN") @@ -24,9 +25,9 @@ def remove_vlan(self, vlan): tbl._del("Vlan" + vlan) time.sleep(1) - def create_vlan_member(self, vlan, interface): + def create_vlan_member(self, vlan, interface, tagging_mode="untagged"): tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") - fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + fvs = swsscommon.FieldValuePairs([("tagging_mode", tagging_mode)]) tbl.set("Vlan" + vlan + "|" + interface, fvs) time.sleep(1) @@ -35,18 +36,69 @@ def remove_vlan_member(self, vlan, interface): tbl._del("Vlan" + vlan + "|" + interface) time.sleep(1) + def create_port_channel(self, dvs, channel, admin_status="up", mtu="1500"): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", admin_status), ("mtu", mtu)]) + tbl.set("PortChannel" + channel, fvs) + dvs.runcmd("ip link add PortChannel" + channel + " type bond") + tbl = swsscommon.Table(self.sdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("state", "ok")]) + tbl.set("PortChannel" + channel, fvs) + time.sleep(1) + + def remove_port_channel(self, dvs, channel): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") + tbl._del("PortChannel" + channel) + dvs.runcmd("ip link del PortChannel" + channel) + tbl = swsscommon.Table(self.sdb, "LAG_TABLE") + tbl._del("PortChannel" + channel) + time.sleep(1) + + def create_port_channel_member(self, channel, interface, status="enabled"): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", status)]) + tbl.set("PortChannel" + channel + ":" + interface, fvs) + time.sleep(1) + + def remove_port_channel_member(self, channel, interface): + tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_MEMBER_TABLE") + tbl._del("PortChannel" + channel + ":" + interface) + time.sleep(1) + def check_syslog(self, dvs, marker, process, err_log, vlan_str, expected_cnt): - (exitcode, num) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep %s | grep \"%s\" | grep -i %s | wc -l" % (marker, process, err_log, vlan_str)]) + (exitcode, num) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep %s | grep \"%s\" | grep -i \"%s\" | wc -l" % (marker, process, err_log, vlan_str)]) assert num.strip() == str(expected_cnt) + def check_app_db_vlan_fields(self, fvs, admin_status="up", mtu="9100"): + for fv in fvs: + if fv[0] == "admin_status": + assert fv[1] == admin_status + elif fv[0] == "mtu": + assert fv[1] == mtu + + def check_app_db_vlan_member_fields(self, fvs, tagging_mode="untagged"): + for fv in fvs: + if fv[0] == "tagging_mode": + assert fv[1] == tagging_mode + + def check_state_db_vlan_fields(self, fvs, state="ok"): + for fv in fvs: + if fv[0] == "state": + assert fv[1] == state + + def check_state_db_vlan_member_fields(self, fvs, state="ok"): + for fv in fvs: + if fv[0] == "state": + assert fv[1] == state + def test_VlanAddRemove(self, dvs, testlog): - self.setup_db(dvs) + dvs.setup_db() # create vlan - self.create_vlan("2") + dvs.create_vlan("2") # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 1 vlan_oid = vlan_entries[0] @@ -58,11 +110,11 @@ def test_VlanAddRemove(self, dvs, testlog): assert fv[1] == "2" # create vlan member - self.create_vlan_member("2", "Ethernet0") + dvs.create_vlan_member("2", "Ethernet0") # check asic database bridge_port_map = {} - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") bridge_port_entries = tbl.getKeys() for key in bridge_port_entries: (status, fvs) = tbl.get(key) @@ -71,7 +123,7 @@ def test_VlanAddRemove(self, dvs, testlog): if fv[0] == "SAI_BRIDGE_PORT_ATTR_PORT_ID": bridge_port_map[key] = fv[1] - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 1 @@ -89,7 +141,7 @@ def test_VlanAddRemove(self, dvs, testlog): assert False # check port pvid - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") (status, fvs) = tbl.get(dvs.asicdb.portnamemap["Ethernet0"]) assert status == True assert "SAI_PORT_ATTR_PORT_VLAN_ID" in [fv[0] for fv in fvs] @@ -98,7 +150,7 @@ def test_VlanAddRemove(self, dvs, testlog): assert fv[1] == "2" # check host interface vlan tag - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF") (status, fvs) = tbl.get(dvs.asicdb.hostifnamemap["Ethernet0"]) assert status == True assert "SAI_HOSTIF_ATTR_VLAN_TAG" in [fv[0] for fv in fvs] @@ -107,87 +159,95 @@ def test_VlanAddRemove(self, dvs, testlog): assert fv[1] == "SAI_HOSTIF_VLAN_TAG_KEEP" # remove vlan member - self.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan_member("2", "Ethernet0") - # remvoe vlan - self.remove_vlan("2") + # remove vlan + dvs.remove_vlan("2") def test_MultipleVlan(self, dvs, testlog): return - self.setup_db(dvs) + dvs.setup_db() # create vlan and vlan members - self.create_vlan("18") - self.create_vlan_member("18", "Ethernet0") - self.create_vlan_member("18", "Ethernet4") - self.create_vlan_member("18", "Ethernet8") + dvs.create_vlan("18") + dvs.create_vlan_member("18", "Ethernet0") + dvs.create_vlan_member("18", "Ethernet4") + dvs.create_vlan_member("18", "Ethernet8") # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 1 - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 3 # remove vlan members - self.remove_vlan_member("18", "Ethernet0") - self.remove_vlan_member("18", "Ethernet4") - self.remove_vlan_member("18", "Ethernet8") + dvs.remove_vlan_member("18", "Ethernet0") + dvs.remove_vlan_member("18", "Ethernet4") + dvs.remove_vlan_member("18", "Ethernet8") - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 0 # create vlan and vlan members - self.create_vlan("188") - self.create_vlan_member("188", "Ethernet20") - self.create_vlan_member("188", "Ethernet24") - self.create_vlan_member("188", "Ethernet28") + dvs.create_vlan("188") + dvs.create_vlan_member("188", "Ethernet20") + dvs.create_vlan_member("188", "Ethernet24") + dvs.create_vlan_member("188", "Ethernet28") # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 2 - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 3 # create vlan members - self.create_vlan_member("18", "Ethernet40") - self.create_vlan_member("18", "Ethernet44") - self.create_vlan_member("18", "Ethernet48") + dvs.create_vlan_member("18", "Ethernet40") + dvs.create_vlan_member("18", "Ethernet44") + dvs.create_vlan_member("18", "Ethernet48") - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 6 # remove vlan members - self.remove_vlan_member("18", "Ethernet40") - self.remove_vlan_member("18", "Ethernet44") - self.remove_vlan_member("18", "Ethernet48") + dvs.remove_vlan_member("18", "Ethernet40") + dvs.remove_vlan_member("18", "Ethernet44") + dvs.remove_vlan_member("18", "Ethernet48") - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 3 # remove vlan members - self.remove_vlan_member("188", "Ethernet20") - self.remove_vlan_member("188", "Ethernet24") - self.remove_vlan_member("188", "Ethernet28") + dvs.remove_vlan_member("188", "Ethernet20") + dvs.remove_vlan_member("188", "Ethernet24") + dvs.remove_vlan_member("188", "Ethernet28") - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 0 + # member ports should have been detached from bridge master properly + exitcode, output = dvs.runcmd(['sh', '-c', "ip link show Ethernet20 | grep -w master"]) + assert exitcode != 0 + exitcode, output = dvs.runcmd(['sh', '-c', "ip link show Ethernet24 | grep -w master"]) + assert exitcode != 0 + exitcode, output = dvs.runcmd(['sh', '-c', "ip link show Ethernet28 | grep -w master"]) + assert exitcode != 0 + # remove vlans - self.remove_vlan("18") - self.remove_vlan("188") + dvs.remove_vlan("18") + dvs.remove_vlan("188") # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 0 @@ -298,7 +358,7 @@ def test_VlanIncrementalConfig(self, dvs, testlog): # remove vlan member dvs.remove_vlan_member("2", "Ethernet0") - # remvoe vlan + # remove vlan dvs.remove_vlan("2") @@ -310,19 +370,19 @@ def test_VlanIncrementalConfig(self, dvs, testlog): (["Vlan", "5"], 1), ]) def test_AddVlanWithIncorrectKeyPrefix(self, dvs, testlog, test_input, expected): - self.setup_db(dvs) + dvs.setup_db() marker = dvs.add_log_marker() vlan_prefix = test_input[0] vlan = test_input[1] # create vlan - tbl = swsscommon.Table(self.cdb, "VLAN") + tbl = swsscommon.Table(dvs.cdb, "VLAN") fvs = swsscommon.FieldValuePairs([("vlanid", vlan)]) tbl.set(vlan_prefix + vlan, fvs) time.sleep(1) # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == expected @@ -331,7 +391,7 @@ def test_AddVlanWithIncorrectKeyPrefix(self, dvs, testlog, test_input, expected) self.check_syslog(dvs, marker, "vlanmgrd", "Invalid key format. No 'Vlan' prefix:", vlan_prefix+vlan, 1) else: #remove vlan - self.remove_vlan(vlan) + dvs.remove_vlan(vlan) @pytest.mark.skipif(StrictVersion(platform.linux_distribution()[1]) <= StrictVersion('8.9'), reason="Debian 8.9 or before has no support") @pytest.mark.parametrize("test_input, expected", [ @@ -341,19 +401,19 @@ def test_AddVlanWithIncorrectKeyPrefix(self, dvs, testlog, test_input, expected) (["Vlan", "5"], 1), ]) def test_AddVlanWithIncorrectValueType(self, dvs, testlog, test_input, expected): - self.setup_db(dvs) + dvs.setup_db() marker = dvs.add_log_marker() vlan_prefix = test_input[0] vlan = test_input[1] # create vlan - tbl = swsscommon.Table(self.cdb, "VLAN") + tbl = swsscommon.Table(dvs.cdb, "VLAN") fvs = swsscommon.FieldValuePairs([("vlanid", vlan)]) tbl.set(vlan_prefix + vlan, fvs) time.sleep(1) # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == expected @@ -362,40 +422,145 @@ def test_AddVlanWithIncorrectValueType(self, dvs, testlog, test_input, expected) self.check_syslog(dvs, marker, "vlanmgrd", "Invalid key format. Not a number after \'Vlan\' prefix:", vlan_prefix+vlan, 1) else: #remove vlan - self.remove_vlan(vlan) + dvs.remove_vlan(vlan) - def test_AddVlanMemberWithNonExistVlan(self, dvs, testlog): + def test_AddPortChannelToVlan(self, dvs, testlog): self.setup_db(dvs) marker = dvs.add_log_marker() vlan = "2" + channel = "001" + + # create port channel + self.create_port_channel(dvs, channel) + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_entries = tbl.getKeys() + assert len(lag_entries) == 1 + + # add port channel member + self.create_port_channel_member(channel, "Ethernet0") + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER") + lag_member_entries = tbl.getKeys() + assert len(lag_member_entries) == 1 + + (status, fvs) = tbl.get(lag_member_entries[0]) + for fv in fvs: + if fv[0] == "SAI_LAG_MEMBER_ATTR_LAG_ID": + assert fv[1] == lag_entries[0] + elif fv[0] == "SAI_LAG_MEMBER_ATTR_PORT_ID": + assert dvs.asicdb.portoidmap[fv[1]] == "Ethernet0" + else: + assert False + + # create vlan + self.create_vlan(vlan) + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 # create vlan member - self.create_vlan_member(vlan, "Ethernet0") + self.create_vlan_member(vlan, "PortChannel" + channel, "tagged") + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + vlan_member_entries = tbl.getKeys() + assert len(vlan_member_entries) == 1 + + # remove vlan member + self.remove_vlan_member(vlan, "PortChannel" + channel) + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + vlan_member_entries = tbl.getKeys() + assert len(vlan_member_entries) == 0 + + # remove vlan + self.remove_vlan(vlan) # check asic database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 0 - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + # remove trunk member + self.remove_port_channel_member(channel, "Ethernet0") + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER") + lag_member_entries = tbl.getKeys() + assert len(lag_member_entries) == 0 + + # remove trunk + self.remove_port_channel(dvs, channel) + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_entries = tbl.getKeys() + assert len(lag_entries) == 0 + + def test_AddVlanMemberWithNonExistVlan(self, dvs, testlog): + dvs.setup_db() + marker = dvs.add_log_marker() + vlan = "2" + + # create vlan member + dvs.create_vlan_member(vlan, "Ethernet0") + + # check asic database + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 0 + + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") vlan_member_entries = tbl.getKeys() assert len(vlan_member_entries) == 0 # remove vlan member from cfgdb - self.remove_vlan_member(vlan, "Ethernet0") + dvs.remove_vlan_member(vlan, "Ethernet0") def test_RemoveNonexistentVlan(self, dvs, testlog): - self.setup_db(dvs) + dvs.setup_db() marker = dvs.add_log_marker() vlan = "2" # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 0 # remove nonexistent vlan - self.remove_vlan(vlan) + dvs.remove_vlan(vlan) + + # create vlan + dvs.create_vlan(vlan) + + # check asic database + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + + # remove vlan + dvs.remove_vlan(vlan) + + @pytest.mark.skipif(StrictVersion(platform.linux_distribution()[1]) <= StrictVersion('8.9'), reason="Debian 8.9 or before has no support") + @pytest.mark.parametrize("test_input, expected", [ + (["tagging_mode", "untagged"], [1, "SAI_VLAN_TAGGING_MODE_UNTAGGED"]), + (["tagging_mode", "tagged"], [1, "SAI_VLAN_TAGGING_MODE_TAGGED"]), + (["tagging_mode", "priority_tagged"], [1, "SAI_VLAN_TAGGING_MODE_PRIORITY_TAGGED"]), + (["tagging_mode", "unexpected_mode"], [0, ""]), + (["no_tag_mode", ""], [1, "SAI_VLAN_TAGGING_MODE_UNTAGGED"]), + ]) + def test_VlanMemberTaggingMode(self, dvs, testlog, test_input, expected): + self.setup_db(dvs) + tagging_mode_prefix = test_input[0] + tagging_mode = test_input[1] + marker = dvs.add_log_marker() + vlan = "2" # create vlan self.create_vlan(vlan) @@ -404,13 +569,55 @@ def test_RemoveNonexistentVlan(self, dvs, testlog): tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # add vlan member + tbl = swsscommon.Table(self.cdb, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([(tagging_mode_prefix, tagging_mode)]) + tbl.set("Vlan" + vlan + "|" + "Ethernet0", fvs) + time.sleep(1) + + # check asic database + bridge_port_map = {} + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + bridge_port_entries = tbl.getKeys() + for key in bridge_port_entries: + (status, fvs) = tbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_BRIDGE_PORT_ATTR_PORT_ID": + bridge_port_map[key] = fv[1] + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + vlan_member_entries = tbl.getKeys() + assert len(vlan_member_entries) == expected[0] + + if len(vlan_member_entries) == 1: + (status, fvs) = tbl.get(vlan_member_entries[0]) + assert status == True + assert len(fvs) == 3 + for fv in fvs: + if fv[0] == "SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE": + assert fv[1] == expected[1] + elif fv[0] == "SAI_VLAN_MEMBER_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + elif fv[0] == "SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID": + assert dvs.asicdb.portoidmap[bridge_port_map[fv[1]]] == "Ethernet0" + else: + assert False + else: + # check error log + self.check_syslog(dvs, marker, "vlanmgrd", "Wrong tagging_mode", test_input, 1) + + # remove vlan member + self.remove_vlan_member(vlan, "Ethernet0") # remove vlan self.remove_vlan(vlan) @pytest.mark.skip(reason="AddMaxVlan take too long to execute") def test_AddMaxVlan(self, dvs, testlog): - self.setup_db(dvs) + dvs.setup_db() min_vid = 2 max_vid = 4094 @@ -418,22 +625,22 @@ def test_AddMaxVlan(self, dvs, testlog): # create max vlan vlan = min_vid while vlan <= max_vid: - self.create_vlan(str(vlan)) + dvs.create_vlan(str(vlan)) vlan += 1 # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == (4094-1) # remove all vlan vlan = min_vid while vlan <= max_vid: - self.remove_vlan(str(vlan)) + dvs.remove_vlan(str(vlan)) vlan += 1 # check asic database - tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] assert len(vlan_entries) == 0 @@ -465,14 +672,144 @@ def test_RemoveVlanWithRouterInterface(self, dvs, testlog): # one loopback router interface one vlan based router interface assert len(intf_entries) == 2 - # remvoe vlan + # remove vlan dvs.remove_vlan("100") - # check error log - self.check_syslog(dvs, marker, "orchagent", "Failed to remove ref count", "Vlan100", 1) + # check asic database still contains the vlan + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + (status, fvs) = tbl.get(vlan_oid) + assert status == True + for fv in fvs: + if fv[0] == "SAI_VLAN_ATTR_VLAN_ID": + assert fv[1] == "100" # remove IP from interface dvs.remove_ip_address("Vlan100", "20.0.0.8/29") - # remvoe vlan + # remove vlan dvs.remove_vlan("100") + + # check asic database does not contain the vlan anymore + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 0 + + def test_VlanDbData(self, dvs, testlog): + self.setup_db(dvs) + vlan = "2" + + # create vlan + self.create_vlan(vlan) + + # check app database + tbl = swsscommon.Table(self.pdb, "VLAN_TABLE") + vlan_entries = tbl.getKeys() + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + (status, fvs) = tbl.get(vlan_oid) + self.check_app_db_vlan_fields(fvs) + + # check state database + tbl = swsscommon.Table(self.sdb, "VLAN_TABLE") + vlan_entries = tbl.getKeys() + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + (status, fvs) = tbl.get(vlan_oid) + self.check_state_db_vlan_fields(fvs) + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + (status, fvs) = tbl.get(vlan_oid) + assert status == True + for fv in fvs: + if fv[0] == "SAI_VLAN_ATTR_VLAN_ID": + assert fv[1] == vlan + + # remove vlan + self.remove_vlan(vlan) + + @pytest.mark.skipif(StrictVersion(platform.linux_distribution()[1]) <= StrictVersion('8.9'), reason="Debian 8.9 or before has no support") + @pytest.mark.parametrize("test_input, expected", [ + (["untagged"], ["SAI_VLAN_TAGGING_MODE_UNTAGGED"]), + (["tagged"], ["SAI_VLAN_TAGGING_MODE_TAGGED"]), + (["priority_tagged"], ["SAI_VLAN_TAGGING_MODE_PRIORITY_TAGGED"]), + ]) + def test_VlanMemberDbData(self, dvs, testlog, test_input, expected): + self.setup_db(dvs) + vlan = "2" + interface = "Ethernet0" + tagging_mode = test_input[0] + + # create vlan + self.create_vlan(vlan) + + # check asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # create vlan member + self.create_vlan_member(vlan, interface, tagging_mode) + + # check app database + tbl = swsscommon.Table(self.pdb, "VLAN_MEMBER_TABLE") + vlan_member_entries = tbl.getKeys() + assert len(vlan_member_entries) == 1 + vlan_member_oid = vlan_member_entries[0] + + (status, fvs) = tbl.get(vlan_member_oid) + self.check_app_db_vlan_member_fields(fvs, tagging_mode) + + # check state database + tbl = swsscommon.Table(self.sdb, "VLAN_MEMBER_TABLE") + vlan_member_entries = tbl.getKeys() + assert len(vlan_member_entries) == 1 + vlan_member_oid = vlan_member_entries[0] + + (status, fvs) = tbl.get(vlan_member_oid) + self.check_state_db_vlan_member_fields(fvs) + + # check asic database + bridge_port_map = {} + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + bridge_port_entries = tbl.getKeys() + for key in bridge_port_entries: + (status, fvs) = tbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_BRIDGE_PORT_ATTR_PORT_ID": + bridge_port_map[key] = fv[1] + + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + vlan_member_entries = tbl.getKeys() + assert len(vlan_member_entries) == 1 + + (status, fvs) = tbl.get(vlan_member_entries[0]) + assert status == True + assert len(fvs) == 3 + for fv in fvs: + if fv[0] == "SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE": + assert fv[1] == expected[0] + elif fv[0] == "SAI_VLAN_MEMBER_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + elif fv[0] == "SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID": + assert dvs.asicdb.portoidmap[bridge_port_map[fv[1]]] == interface + else: + assert False + + # remove vlan member + self.remove_vlan_member(vlan, interface) + + # remove vlan + self.remove_vlan(vlan) \ No newline at end of file diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 9e8b6116502..5eac68381ce 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -59,15 +59,21 @@ def get_created_entry(db, table, existed_entries): return new_entries[0] -def get_created_entries(db, table, existed_entries, count): +def get_all_created_entries(db, table, existed_entries): tbl = swsscommon.Table(db, table) entries = set(tbl.getKeys()) new_entries = list(entries - existed_entries) - assert len(new_entries) == count, "Wrong number of created entries." + assert len(new_entries) > 0, "No created entries." new_entries.sort() return new_entries +def get_created_entries(db, table, existed_entries, count): + new_entries = get_all_created_entries(db, table, existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + return new_entries + + def get_deleted_entries(db, table, existed_entries, count): tbl = swsscommon.Table(db, table) entries = set(tbl.getKeys()) @@ -291,7 +297,7 @@ def delete_phy_interface(dvs, ifname, ipaddr): time.sleep(2) -def create_vnet_entry(dvs, name, tunnel, vni, peer_list): +def create_vnet_entry(dvs, name, tunnel, vni, peer_list, scope=""): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) @@ -301,6 +307,9 @@ def create_vnet_entry(dvs, name, tunnel, vni, peer_list): ("peer_list", peer_list), ] + if scope: + attrs.append(('scope', scope)) + # create the VXLAN tunnel Term entry in Config DB create_entry_tbl( conf_db, @@ -520,6 +529,14 @@ def check_vnet_entry(self, dvs, name, peer_list=[]): self.vnet_vr_ids.update(new_vr_ids) self.vr_map[name] = { 'ing':new_vr_ids[0], 'egr':new_vr_ids[1], 'peer':peer_list } + def check_default_vnet_entry(self, dvs, name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + #Check virtual router objects + assert how_many_entries_exist(asic_db, self.ASIC_VRF_TABLE) == (len(self.vnet_vr_ids)),\ + "Some VR objects are created" + #Mappers for default VNET is created with default VR objects. + self.vr_map[name] = { 'ing':list(self.vnet_vr_ids)[0], 'egr':list(self.vnet_vr_ids)[0], 'peer':[] } + def check_del_vnet_entry(self, dvs, name): # TODO: Implement for VRF VNET return True @@ -567,8 +584,12 @@ def check_router_interface(self, dvs, name, vlan_oid=0): self.routes.update(new_route) def check_del_router_interface(self, dvs, name): - # TODO: Implement for VRF VNET - return True + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + old_rif = get_deleted_entries(asic_db, self.ASIC_RIF_TABLE, self.rifs, 1) + check_deleted_object(asic_db, self.ASIC_RIF_TABLE, old_rif[0]) + + self.rifs.remove(old_rif[0]) def check_vnet_local_routes(self, dvs, name): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) @@ -800,6 +821,9 @@ def check_vnet_entry(self, dvs, name, peer_list=[]): self.rifs = get_exist_entries(dvs, self.ASIC_RIF_TABLE) self.vnet_map.update({name:{}}) + def check_default_vnet_entry(self, dvs, name): + return self.check_vnet_entry(dvs, name) + def check_del_vnet_entry(self, dvs, name): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) @@ -876,12 +900,16 @@ def check_vnet_routes(self, dvs, name, endpoint, tunnel, mac="", vni=0): _vni = str(vni) if vni != 0 else self.vnet_map[name]['vni'] if (mac,_vni) not in self.vnet_mac_vni_list: - new_fdb = get_created_entry(asic_db, self.ASIC_FDB_ENTRY, self.fdbs) + new_fdbs = get_all_created_entries(asic_db, self.ASIC_FDB_ENTRY, self.fdbs) expected_attrs = { "SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_STATIC", "SAI_FDB_ENTRY_ATTR_ENDPOINT_IP": endpoint } + + new_fdb = next(iter([fdb for fdb in new_fdbs if (mac if mac != "" else "00:00:00:00:00:00") in fdb]), None) + assert new_fdb, "Wrong number of created FDB entries." + check_object(asic_db, self.ASIC_FDB_ENTRY, new_fdb, expected_attrs) self.fdbs.add(new_fdb) @@ -922,7 +950,6 @@ def get_vnet_obj(self): ''' Test 1 - Create Vlan Interface, Tunnel and Vnet ''' - @pytest.mark.skip(reason="Failing. Under investigation") def test_vnet_orch_1(self, dvs, testlog): vnet_obj = self.get_vnet_obj() @@ -1285,3 +1312,19 @@ def test_vnet_orch_4(self, dvs, testlog): delete_vnet_entry(dvs, 'Vnet3001') vnet_obj.check_del_vnet_entry(dvs, 'Vnet3001') + + ''' + Test 5 - Default VNet test + ''' + def test_vnet_orch_5(self, dvs, testlog): + vnet_obj = self.get_vnet_obj() + + tunnel_name = 'tunnel_5' + + vnet_obj.fetch_exist_entries(dvs) + + create_vxlan_tunnel(dvs, tunnel_name, '8.8.8.8') + create_vnet_entry(dvs, 'Vnet_5', tunnel_name, '4789', "", 'default') + + vnet_obj.check_default_vnet_entry(dvs, 'Vnet_5') + vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_5', '4789') diff --git a/tests/test_vrf.py b/tests/test_vrf.py index 3dec3e0dc19..a53d498b98c 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -150,90 +150,90 @@ def packet_action_gen(): r = random.choice(values) return r[0], r[1] - -def test_VRFOrch_Comprehensive(dvs, testlog): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - attributes = [ - ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), - ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), - ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), - ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), - ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), - ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), - ] - - random.seed(int(time.clock())) - - for n in xrange(2**len(attributes)): - # generate testcases for all combinations of attributes +class TestVrf(object): + def test_VRFOrch_Comprehensive(self, dvs, testlog): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + attributes = [ + ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), + ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), + ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), + ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), + ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), + ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), + ] + + random.seed(int(time.clock())) + + for n in xrange(2**len(attributes)): + # generate testcases for all combinations of attributes + req_attr = [] + exp_attr = {} + vrf_name = "vrf_%d" % n + bmask = 0x1 + for an in xrange(len(attributes)): + if (bmask & n) > 0: + req_res, exp_res = attributes[an][2]() + req_attr.append((attributes[an][0], req_res)) + exp_attr[attributes[an][1]] = exp_res + bmask <<= 1 + state = vrf_create(asic_db, appl_db, vrf_name, req_attr, exp_attr) + vrf_remove(asic_db, appl_db, vrf_name, state) + + + def test_VRFOrch(self, dvs, testlog): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + state = vrf_create(asic_db, appl_db, "vrf0", + [ + ], + { + } + ) + vrf_remove(asic_db, appl_db, "vrf0", state) + + state = vrf_create(asic_db, appl_db, "vrf1", + [ + ('v4', 'true'), + ('src_mac', '02:04:06:07:08:09'), + ], + { + 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE': 'true', + 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS': '02:04:06:07:08:09', + } + ) + vrf_remove(asic_db, appl_db, "vrf1", state) + + def test_VRFOrch_Update(self, dvs, testlog): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + attributes = [ + ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), + ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), + ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), + ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), + ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), + ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), + ] + + random.seed(int(time.clock())) + + state = vrf_create(asic_db, appl_db, "vrf_a", + [ + ], + { + } + ) + + # try to update each attribute req_attr = [] exp_attr = {} - vrf_name = "vrf_%d" % n - bmask = 0x1 - for an in xrange(len(attributes)): - if (bmask & n) > 0: - req_res, exp_res = attributes[an][2]() - req_attr.append((attributes[an][0], req_res)) - exp_attr[attributes[an][1]] = exp_res - bmask <<= 1 - state = vrf_create(asic_db, appl_db, vrf_name, req_attr, exp_attr) - vrf_remove(asic_db, appl_db, vrf_name, state) - - -def test_VRFOrch(dvs, testlog): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - state = vrf_create(asic_db, appl_db, "vrf0", - [ - ], - { - } - ) - vrf_remove(asic_db, appl_db, "vrf0", state) - - state = vrf_create(asic_db, appl_db, "vrf1", - [ - ('v4', 'true'), - ('src_mac', '02:04:06:07:08:09'), - ], - { - 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE': 'true', - 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS': '02:04:06:07:08:09', - } - ) - vrf_remove(asic_db, appl_db, "vrf1", state) - -def test_VRFOrch_Update(dvs, testlog): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - attributes = [ - ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), - ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), - ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), - ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), - ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), - ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), - ] - - random.seed(int(time.clock())) - - state = vrf_create(asic_db, appl_db, "vrf_a", - [ - ], - { - } - ) - - # try to update each attribute - req_attr = [] - exp_attr = {} - for attr in attributes: - req_res, exp_res = attr[2]() - req_attr.append((attr[0], req_res)) - exp_attr[attr[1]] = exp_res - vrf_update(asic_db, appl_db, "vrf_a", req_attr, exp_attr, state) + for attr in attributes: + req_res, exp_res = attr[2]() + req_attr.append((attr[0], req_res)) + exp_attr[attr[1]] = exp_res + vrf_update(asic_db, appl_db, "vrf_a", req_attr, exp_attr, state) - vrf_remove(asic_db, appl_db, "vrf_a", state) + vrf_remove(asic_db, appl_db, "vrf_a", state) diff --git a/tests/test_vxlan_tunnel.py b/tests/test_vxlan_tunnel.py index c1306b89667..225a913bb6d 100644 --- a/tests/test_vxlan_tunnel.py +++ b/tests/test_vxlan_tunnel.py @@ -241,69 +241,69 @@ def get_lo(dvs): return lo_id +class TestVxlan(object): + def test_vxlan_term_orch(self, dvs, testlog): + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_ids = set() + tunnel_term_ids = set() + tunnel_map_map = {} + vlan_ids = get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + loopback_id = get_lo(dvs) -def test_vxlan_term_orch(dvs, testlog): - tunnel_map_ids = set() - tunnel_map_entry_ids = set() - tunnel_ids = set() - tunnel_term_ids = set() - tunnel_map_map = {} - vlan_ids = get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - loopback_id = get_lo(dvs) + create_vlan(dvs, "Vlan50", vlan_ids) + create_vlan(dvs, "Vlan51", vlan_ids) + create_vlan(dvs, "Vlan52", vlan_ids) + create_vlan(dvs, "Vlan53", vlan_ids) + create_vlan(dvs, "Vlan54", vlan_ids) + create_vlan(dvs, "Vlan55", vlan_ids) + create_vlan(dvs, "Vlan56", vlan_ids) + create_vlan(dvs, "Vlan57", vlan_ids) - create_vlan(dvs, "Vlan50", vlan_ids) - create_vlan(dvs, "Vlan51", vlan_ids) - create_vlan(dvs, "Vlan52", vlan_ids) - create_vlan(dvs, "Vlan53", vlan_ids) - create_vlan(dvs, "Vlan54", vlan_ids) - create_vlan(dvs, "Vlan55", vlan_ids) - create_vlan(dvs, "Vlan56", vlan_ids) - create_vlan(dvs, "Vlan57", vlan_ids) + create_vxlan_tunnel(dvs, 'tunnel_1', '10.0.0.1', '100.100.100.1', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - create_vxlan_tunnel(dvs, 'tunnel_1', '10.0.0.1', '100.100.100.1', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_1', tunnel_map_map, 'Vlan50', '850', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_1', tunnel_map_map, 'Vlan50', '850', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + tunnel_map_map['tunnel_1'] = check_vxlan_tunnel(dvs,'10.0.0.1', '100.100.100.1', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - tunnel_map_map['tunnel_1'] = check_vxlan_tunnel(dvs,'10.0.0.1', '100.100.100.1', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel(dvs, 'tunnel_2', '11.0.0.2', '101.101.101.2', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - create_vxlan_tunnel(dvs, 'tunnel_2', '11.0.0.2', '101.101.101.2', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_1', tunnel_map_map, 'Vlan51', '851', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_1', tunnel_map_map, 'Vlan51', '851', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + tunnel_map_map['tunnel_2'] = check_vxlan_tunnel(dvs,'11.0.0.2', '101.101.101.2', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - tunnel_map_map['tunnel_2'] = check_vxlan_tunnel(dvs,'11.0.0.2', '101.101.101.2', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel(dvs, 'tunnel_3', '12.0.0.3', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - create_vxlan_tunnel(dvs, 'tunnel_3', '12.0.0.3', '0.0.0.0', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_1', tunnel_map_map, 'Vlan52', '852', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_1', tunnel_map_map, 'Vlan52', '852', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + tunnel_map_map['tunnel_3'] = check_vxlan_tunnel(dvs, '12.0.0.3', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - tunnel_map_map['tunnel_3'] = check_vxlan_tunnel(dvs, '12.0.0.3', '0.0.0.0', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel(dvs, 'tunnel_4', '15.0.0.5', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id, True) - create_vxlan_tunnel(dvs, 'tunnel_4', '15.0.0.5', '0.0.0.0', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id, True) + create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_1', tunnel_map_map, 'Vlan53', '853', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_1', tunnel_map_map, 'Vlan53', '853', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + tunnel_map_map['tunnel_4'] = check_vxlan_tunnel(dvs, '15.0.0.5', '0.0.0.0', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) - tunnel_map_map['tunnel_4'] = check_vxlan_tunnel(dvs, '15.0.0.5', '0.0.0.0', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids, loopback_id) + create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_2', tunnel_map_map, 'Vlan54', '854', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_1', 'entry_2', tunnel_map_map, 'Vlan54', '854', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_2', tunnel_map_map, 'Vlan55', '855', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_2', 'entry_2', tunnel_map_map, 'Vlan55', '855', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_2', tunnel_map_map, 'Vlan56', '856', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - create_vxlan_tunnel_entry(dvs, 'tunnel_3', 'entry_2', tunnel_map_map, 'Vlan56', '856', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) - - create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_2', tunnel_map_map, 'Vlan57', '857', - tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) + create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_2', tunnel_map_map, 'Vlan57', '857', + tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index e87c644b47f..1b958a1d845 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -1,3 +1,4 @@ + from swsscommon import swsscommon import os import re @@ -5,6 +6,14 @@ import json import pytest + +# macros for number of interfaces and number of neighbors +# TBD: NUM_NEIGH_PER_INTF >= 128 ips will cause test framework to hang by default kernel settings +# TBD: Need tune gc_thresh1/2/3 at host side of vs docker to support this. +NUM_INTF = 8 +NUM_NEIGH_PER_INTF = 16 #128 +NUM_OF_NEIGHS = (NUM_INTF*NUM_NEIGH_PER_INTF) + # Get restore count of all processes supporting warm restart def swss_get_RestoreCount(dvs, state_db): restore_count = {} @@ -106,213 +115,6 @@ def how_many_entries_exist(db, table): tbl = swsscommon.Table(db, table) return len(tbl.getKeys()) -def test_PortSyncdWarmRestart(dvs, testlog): - - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - dvs.runcmd("config warm_restart enable swss") - - dvs.runcmd("ifconfig Ethernet16 up") - dvs.runcmd("ifconfig Ethernet20 up") - - time.sleep(1) - - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet16|11.0.0.1/29", fvs) - intf_tbl.set("Ethernet20|11.0.0.9/29", fvs) - intf_tbl.set("Ethernet16", fvs) - intf_tbl.set("Ethernet20", fvs) - dvs.runcmd("ifconfig Ethernet16 up") - dvs.runcmd("ifconfig Ethernet20 up") - - dvs.servers[4].runcmd("ip link set down dev eth0") == 0 - dvs.servers[4].runcmd("ip link set up dev eth0") == 0 - dvs.servers[4].runcmd("ifconfig eth0 11.0.0.2/29") - dvs.servers[4].runcmd("ip route add default via 11.0.0.1") - - dvs.servers[5].runcmd("ip link set down dev eth0") == 0 - dvs.servers[5].runcmd("ip link set up dev eth0") == 0 - dvs.servers[5].runcmd("ifconfig eth0 11.0.0.10/29") - dvs.servers[5].runcmd("ip route add default via 11.0.0.9") - - time.sleep(1) - - # Ethernet port oper status should be up - check_port_oper_status(appl_db, "Ethernet16", "up") - check_port_oper_status(appl_db, "Ethernet20", "up") - - # Ping should work between servers via vs vlan interfaces - ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.10") - time.sleep(1) - - neighTbl = swsscommon.Table(appl_db, "NEIGH_TABLE") - (status, fvs) = neighTbl.get("Ethernet16:11.0.0.2") - assert status == True - - (status, fvs) = neighTbl.get("Ethernet20:11.0.0.10") - assert status == True - - restore_count = swss_get_RestoreCount(dvs, state_db) - - # restart portsyncd - dvs.runcmd(['sh', '-c', 'pkill -x portsyncd']) - - pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE") - dvs.runcmd(['sh', '-c', 'supervisorctl start portsyncd']) - - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == 0 - - #new ip on server 5 - dvs.servers[5].runcmd("ifconfig eth0 11.0.0.11/29") - - # Ping should work between servers via vs Ethernet interfaces - ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.11") - - # new neighbor learn on VS - (status, fvs) = neighTbl.get("Ethernet20:11.0.0.11") - assert status == True - - # Port state change reflected in appDB correctly - dvs.servers[6].runcmd("ip link set down dev eth0") == 0 - dvs.servers[6].runcmd("ip link set up dev eth0") == 0 - time.sleep(1) - - check_port_oper_status(appl_db, "Ethernet16", "up") - check_port_oper_status(appl_db, "Ethernet20", "up") - check_port_oper_status(appl_db, "Ethernet24", "up") - - - swss_app_check_RestoreCount_single(state_db, restore_count, "portsyncd") - - intf_tbl._del("Ethernet16|11.0.0.1/29") - intf_tbl._del("Ethernet20|11.0.0.9/29") - intf_tbl._del("Ethernet16") - intf_tbl._del("Ethernet20") - time.sleep(2) - - -def test_VlanMgrdWarmRestart(dvs, testlog): - - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - dvs.runcmd("ifconfig Ethernet16 0") - dvs.runcmd("ifconfig Ethernet20 0") - - dvs.runcmd("ifconfig Ethernet16 up") - dvs.runcmd("ifconfig Ethernet20 up") - - time.sleep(1) - - dvs.runcmd("config warm_restart enable swss") - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", "Vlan16", - [ - ("vlanid", "16"), - ] - ) - # create vlan - create_entry_tbl( - conf_db, - "VLAN", "Vlan20", - [ - ("vlanid", "20"), - ] - ) - # create vlan member entry in config db. Don't use Ethernet0/4/8/12 as IP configured on them in previous testing. - create_entry_tbl( - conf_db, - "VLAN_MEMBER", "Vlan16|Ethernet16", - [ - ("tagging_mode", "untagged"), - ] - ) - - create_entry_tbl( - conf_db, - "VLAN_MEMBER", "Vlan20|Ethernet20", - [ - ("tagging_mode", "untagged"), - ] - ) - - time.sleep(1) - - intf_tbl = swsscommon.Table(conf_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Vlan16|11.0.0.1/29", fvs) - intf_tbl.set("Vlan20|11.0.0.9/29", fvs) - intf_tbl.set("Vlan16", fvs) - intf_tbl.set("Vlan20", fvs) - dvs.runcmd("ifconfig Vlan16 up") - dvs.runcmd("ifconfig Vlan20 up") - - dvs.servers[4].runcmd("ifconfig eth0 11.0.0.2/29") - dvs.servers[4].runcmd("ip route add default via 11.0.0.1") - - dvs.servers[5].runcmd("ifconfig eth0 11.0.0.10/29") - dvs.servers[5].runcmd("ip route add default via 11.0.0.9") - - time.sleep(1) - - # Ping should work between servers via vs vlan interfaces - ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.10") - time.sleep(1) - - tbl = swsscommon.Table(appl_db, "NEIGH_TABLE") - (status, fvs) = tbl.get("Vlan16:11.0.0.2") - assert status == True - - (status, fvs) = tbl.get("Vlan20:11.0.0.10") - assert status == True - - (exitcode, bv_before) = dvs.runcmd("bridge vlan") - print(bv_before) - - restore_count = swss_get_RestoreCount(dvs, state_db) - - dvs.runcmd(['sh', '-c', 'pkill -x vlanmgrd']) - - pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE") - - dvs.runcmd(['sh', '-c', 'supervisorctl start vlanmgrd']) - time.sleep(2) - - (exitcode, bv_after) = dvs.runcmd("bridge vlan") - assert bv_after == bv_before - - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub, ignore=["SAI_OBJECT_TYPE_FDB_ENTRY"]) - assert nadd == 0 - assert ndel == 0 - - #new ip on server 5 - dvs.servers[5].runcmd("ifconfig eth0 11.0.0.11/29") - - # Ping should work between servers via vs vlan interfaces - ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.11") - - # new neighbor learn on VS - (status, fvs) = tbl.get("Vlan20:11.0.0.11") - assert status == True - - swss_app_check_RestoreCount_single(state_db, restore_count, "vlanmgrd") - - intf_tbl._del("Vlan16|11.0.0.1/29") - intf_tbl._del("Vlan20|11.0.0.9/29") - intf_tbl._del("Vlan16") - intf_tbl._del("Vlan20") - time.sleep(2) - def stop_neighsyncd(dvs): dvs.runcmd(['sh', '-c', 'pkill -x neighsyncd']) @@ -384,593 +186,6 @@ def check_syslog_for_neighbor_entry(dvs, marker, new_cnt, delete_cnt, iptype): else: assert "iptype is unknown" == "" -def test_swss_neighbor_syncup(dvs, testlog): - - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - dvs.runcmd("config warm_restart enable swss") - - # - # Testcase1: - # Add neighbor entries in linux kernel, appDB should get all of them - # - - # create neighbor entries (4 ipv4 and 4 ip6, two each on each interface) in linux kernel - intfs = ["Ethernet24", "Ethernet28"] - - for intf in intfs: - # set timeout to be the same as real HW - dvs.runcmd("sysctl -w net.ipv4.neigh.{}.base_reachable_time_ms=1800000".format(intf)) - dvs.runcmd("sysctl -w net.ipv6.neigh.{}.base_reachable_time_ms=1800000".format(intf)) - - #enable ipv6 on docker - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("{}|24.0.0.1/24".format(intfs[0]), fvs) - intf_tbl.set("{}|28.0.0.9/24".format(intfs[1]), fvs) - intf_tbl.set("{}|2400::1/64".format(intfs[0]), fvs) - intf_tbl.set("{}|2800::1/64".format(intfs[1]), fvs) - intf_tbl.set("{}".format(intfs[0]), fvs) - intf_tbl.set("{}".format(intfs[1]), fvs) - intf_tbl.set("{}".format(intfs[0]), fvs) - intf_tbl.set("{}".format(intfs[1]), fvs) - dvs.runcmd("ifconfig {} up".format(intfs[0])) - dvs.runcmd("ifconfig {} up".format(intfs[1])) - - ips = ["24.0.0.2", "24.0.0.3", "28.0.0.2", "28.0.0.3"] - v6ips = ["2400::2", "2400::3", "2800::2", "2800::3"] - - macs = ["00:00:00:00:24:02", "00:00:00:00:24:03", "00:00:00:00:28:02", "00:00:00:00:28:03"] - - for i in range(len(ips)): - dvs.runcmd("ip neigh add {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], macs[i])) - - for i in range(len(v6ips)): - dvs.runcmd("ip -6 neigh add {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], macs[i])) - - time.sleep(1) - - # Check the neighbor entries are inserted correctly - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "NEIGH_TABLE") - - for i in range(len(ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) - assert status == True - - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv4" - - for i in range(len(v6ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) - assert status == True - - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv6" - - # - # Testcase 2: - # Restart neighsyncd without change neighbor entries, nothing should be sent to appDB or sairedis, - # appDB should be kept the same. - # - - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) - - # stop neighsyncd and sairedis.rec - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - marker = dvs.add_log_marker() - pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") - start_neighsyncd(dvs) - start_restore_neighbors(dvs) - time.sleep(10) - - # Check the neighbor entries are still in appDB correctly - for i in range(len(ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) - assert status == True - - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv4" - - for i in range(len(v6ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) - assert status == True - - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv6" - - # check syslog and sairedis.rec file for activities - check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == 0 - - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - - # - # Testcase 3: - # stop neighsyncd, delete even nummber ipv4/ipv6 neighbor entries from each interface, warm start neighsyncd. - # the neighsyncd is supposed to sync up the entries from kernel after warm restart - # note: there was an issue for neighbor delete, it will be marked as FAILED instead of deleted in kernel - # but it will send netlink message to be removed from appDB, so it works ok here, - # just that if we want to add the same neighbor again, use "change" instead of "add" - - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) - - # stop neighsyncd - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - marker = dvs.add_log_marker() - - # delete even nummber of ipv4/ipv6 neighbor entries from each interface - for i in range(0, len(ips), 2): - dvs.runcmd("ip neigh del {} dev {}".format(ips[i], intfs[i/2])) - - for i in range(0, len(v6ips), 2): - dvs.runcmd("ip -6 neigh del {} dev {}".format(v6ips[i], intfs[i/2])) - - # start neighsyncd again - start_neighsyncd(dvs) - start_restore_neighbors(dvs) - time.sleep(10) - - # check ipv4 and ipv6 neighbors - for i in range(len(ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) - #should not see deleted neighbor entries - if i % 2 == 0: - assert status == False - continue - else: - assert status == True - - #undeleted entries should still be there. - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv4" - - for i in range(len(v6ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) - #should not see deleted neighbor entries - if i % 2 == 0: - assert status == False - continue - else: - assert status == True - - #undeleted entries should still be there. - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv6" - - # check syslog and sairedis.rec file for activities - # 2 deletes each for ipv4 and ipv6 - # 4 neighbor removal in asic db - check_syslog_for_neighbor_entry(dvs, marker, 0, 2, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 0, 2, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == 4 - - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - - - # - # Testcase 4: - # Stop neighsyncd, add even nummber of ipv4/ipv6 neighbor entries to each interface again, - # Start neighsyncd - # The neighsyncd is supposed to sync up the entries from kernel after warm restart - # Check the timer is not retrieved from configDB since it is not configured - - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) - - # stop neighsyncd - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - marker = dvs.add_log_marker() - - # add even nummber of ipv4/ipv6 neighbor entries to each interface - # use "change" if neighbor is in FAILED state - for i in range(0, len(ips), 2): - (rc, output) = dvs.runcmd(['sh', '-c', "ip -4 neigh | grep {}".format(ips[i])]) - print output - if output: - dvs.runcmd("ip neigh change {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], macs[i])) - else: - dvs.runcmd("ip neigh add {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], macs[i])) - - for i in range(0, len(v6ips), 2): - (rc, output) = dvs.runcmd(['sh', '-c', "ip -6 neigh | grep {}".format(v6ips[i])]) - print output - if output: - dvs.runcmd("ip -6 neigh change {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], macs[i])) - else: - dvs.runcmd("ip -6 neigh add {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], macs[i])) - - # start neighsyncd again - start_neighsyncd(dvs) - start_restore_neighbors(dvs) - time.sleep(10) - - # no neighsyncd timer configured - check_no_neighsyncd_timer(dvs) - - # check ipv4 and ipv6 neighbors, should see all neighbors - for i in range(len(ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) - assert status == True - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv4" - - for i in range(len(v6ips)): - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) - assert status == True - for v in fvs: - if v[0] == "neigh": - assert v[1] == macs[i] - if v[0] == "family": - assert v[1] == "IPv6" - - # check syslog and asic db for activities - # 2 news entries for ipv4 and ipv6 each - # 4 neighbor creation in asic db - check_syslog_for_neighbor_entry(dvs, marker, 2, 0, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 2, 0, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 4 - assert ndel == 0 - - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - - # - # Testcase 5: - # Even number of ip4/6 neigbors updated with new mac. - # Odd number of ipv4/6 neighbors removed - # neighbor syncd should sync it up after warm restart - # include the timer settings in this testcase - - # setup timer in configDB - timer_value = "15" - - dvs.runcmd("config warm_restart neighsyncd_timer {}".format(timer_value)) - - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) - - # stop neighsyncd - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - marker = dvs.add_log_marker() - - # Even number of ip4/6 neigbors updated with new mac. - # Odd number of ipv4/6 neighbors removed - newmacs = ["00:00:00:01:12:02", "00:00:00:01:12:03", "00:00:00:01:16:02", "00:00:00:01:16:03"] - - for i in range(len(ips)): - if i % 2 == 0: - dvs.runcmd("ip neigh change {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], newmacs[i])) - else: - dvs.runcmd("ip neigh del {} dev {}".format(ips[i], intfs[i/2])) - - for i in range(len(v6ips)): - if i % 2 == 0: - dvs.runcmd("ip -6 neigh change {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], newmacs[i])) - else: - dvs.runcmd("ip -6 neigh del {} dev {}".format(v6ips[i], intfs[i/2])) - - # start neighsyncd again - start_neighsyncd(dvs) - start_restore_neighbors(dvs) - time.sleep(10) - - # timer is not expired yet, state should be "restored" - swss_app_check_warmstart_state(state_db, "neighsyncd", "restored") - time.sleep(10) - - # check neigh syncd timer is retrived from configDB - check_neighsyncd_timer(dvs, timer_value) - - # check ipv4 and ipv6 neighbors, should see all neighbors with updated info - for i in range(len(ips)): - if i % 2 == 0: - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) - assert status == True - for v in fvs: - if v[0] == "neigh": - assert v[1] == newmacs[i] - if v[0] == "family": - assert v[1] == "IPv4" - else: - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) - assert status == False - - for i in range(len(v6ips)): - if i % 2 == 0: - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) - assert status == True - for v in fvs: - if v[0] == "neigh": - assert v[1] == newmacs[i] - if v[0] == "family": - assert v[1] == "IPv6" - else: - (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) - assert status == False - - time.sleep(2) - - # check syslog and asic db for activities - # 2 news, 2 deletes for ipv4 and ipv6 each - # 4 set, 4 removes for neighbor in asic db - check_syslog_for_neighbor_entry(dvs, marker, 2, 2, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 2, 2, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 4 - assert ndel == 4 - - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - - # post-cleanup - dvs.runcmd("ip -s neigh flush all") - dvs.runcmd("ip -6 -s neigh flush all") - - intf_tbl._del("{}|24.0.0.1/24".format(intfs[0])) - intf_tbl._del("{}|28.0.0.9/24".format(intfs[1])) - intf_tbl._del("{}|2400::1/64".format(intfs[0])) - intf_tbl._del("{}|2800::1/64".format(intfs[1])) - intf_tbl._del("{}".format(intfs[0])) - intf_tbl._del("{}".format(intfs[1])) - intf_tbl._del("{}".format(intfs[0])) - intf_tbl._del("{}".format(intfs[1])) - time.sleep(2) - - -# TODO: The condition of warm restart readiness check is still under discussion. -def test_OrchagentWarmRestartReadyCheck(dvs, testlog): - - time.sleep(1) - - dvs.runcmd("config warm_restart enable swss") - - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - intf_tbl = swsscommon.Table(config_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") - - dvs.servers[0].runcmd("ifconfig eth0 10.0.0.1/31") - dvs.servers[0].runcmd("ip route add default via 10.0.0.0") - - dvs.servers[1].runcmd("ifconfig eth0 10.0.0.3/31") - dvs.servers[1].runcmd("ip route add default via 10.0.0.2") - - - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - ps = swsscommon.ProducerStateTable(appl_db, swsscommon.APP_ROUTE_TABLE_NAME) - fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) - - ps.set("2.2.2.0/24", fvs) - - time.sleep(1) - # Should fail, since neighbor for next 10.0.0.1 has not been not resolved yet - (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") - assert result == "RESTARTCHECK failed\n" - - # Should succeed, the option for skipPendingTaskCheck -s and noFreeze -n have been provided. - # Wait up to 500 milliseconds for response from orchagent. Default wait time is 1000 milliseconds. - (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check -n -s -w 500") - assert result == "RESTARTCHECK succeeded\n" - - # get neighbor and arp entry - dvs.servers[1].runcmd("ping -c 1 10.0.0.1") - - time.sleep(1) - (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") - assert result == "RESTARTCHECK succeeded\n" - - # Should fail since orchagent has been frozen at last step. - (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check -n -s -w 500") - assert result == "RESTARTCHECK failed\n" - - # Cleaning previously pushed route-entry to ease life of subsequent testcases. - ps._del("2.2.2.0/24") - time.sleep(1) - - intf_tbl._del("Ethernet0|10.0.0.0/31") - intf_tbl._del("Ethernet4|10.0.0.2/31") - intf_tbl._del("Ethernet0") - intf_tbl._del("Ethernet4") - time.sleep(2) - - # recover for test cases after this one. - dvs.stop_swss() - dvs.start_swss() - time.sleep(5) - -def test_swss_port_state_syncup(dvs, testlog): - - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - dvs.runcmd("config warm_restart enable swss") - - tbl = swsscommon.Table(appl_db, swsscommon.APP_PORT_TABLE_NAME) - - restore_count = swss_get_RestoreCount(dvs, state_db) - - # update port admin state - intf_tbl = swsscommon.Table(conf_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - intf_tbl.set("Ethernet8|10.0.0.4/31", fvs) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - intf_tbl.set("Ethernet8", fvs) - dvs.runcmd("ifconfig Ethernet0 up") - dvs.runcmd("ifconfig Ethernet4 up") - dvs.runcmd("ifconfig Ethernet8 up") - - dvs.runcmd("arp -s 10.0.0.1 00:00:00:00:00:01") - dvs.runcmd("arp -s 10.0.0.3 00:00:00:00:00:02") - dvs.runcmd("arp -s 10.0.0.5 00:00:00:00:00:03") - - dvs.servers[0].runcmd("ip link set down dev eth0") == 0 - dvs.servers[1].runcmd("ip link set down dev eth0") == 0 - dvs.servers[2].runcmd("ip link set down dev eth0") == 0 - - dvs.servers[2].runcmd("ip link set up dev eth0") == 0 - - time.sleep(3) - - for i in [0, 1, 2]: - (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) - assert status == True - oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break - if i == 2: - assert oper_status == "up" - else: - assert oper_status == "down" - - intf_tbl._del("Ethernet0|10.0.0.0/31") - intf_tbl._del("Ethernet4|10.0.0.2/31") - intf_tbl._del("Ethernet8|10.0.0.4/31") - intf_tbl._del("Ethernet0") - intf_tbl._del("Ethernet4") - intf_tbl._del("Ethernet8") - time.sleep(2) - - dvs.stop_swss() - time.sleep(3) - - # flap the port oper status for Ethernet0, Ethernet4 and Ethernet8 - dvs.servers[0].runcmd("ip link set down dev eth0") == 0 - dvs.servers[1].runcmd("ip link set down dev eth0") == 0 - dvs.servers[2].runcmd("ip link set down dev eth0") == 0 - - dvs.servers[0].runcmd("ip link set up dev eth0") == 0 - dvs.servers[1].runcmd("ip link set up dev eth0") == 0 - - time.sleep(5) - dbobjs =[(swsscommon.APPL_DB, swsscommon.APP_PORT_TABLE_NAME + ":*"), \ - (swsscommon.STATE_DB, swsscommon.STATE_WARM_RESTART_TABLE_NAME + "|orchagent")] - pubsubDbs = dvs.SubscribeDbObjects(dbobjs) - dvs.start_swss() - start_restore_neighbors(dvs) - time.sleep(10) - - swss_check_RestoreCount(dvs, state_db, restore_count) - - intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - intf_tbl.set("Ethernet8|10.0.0.4/31", fvs) - intf_tbl.set("Ethernet0", fvs) - intf_tbl.set("Ethernet4", fvs) - intf_tbl.set("Ethernet8", fvs) - time.sleep(3) - - for i in [0, 1, 2]: - (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) - assert status == True - oper_status = "unknown" - for v in fvs: - if v[0] == "oper_status": - oper_status = v[1] - break - if i == 2: - assert oper_status == "down" - else: - assert oper_status == "up" - - # check the pubsub messages. - # No appDB port table operation should exist before orchagent state restored flag got set. - # appDB port table status sync up happens before WARM_RESTART_TABLE reconciled flag is set - # pubsubMessages is an ordered list of pubsub messages. - pubsubMessages = dvs.GetSubscribedMessages(pubsubDbs) - - portOperStatusChanged = False - # number of times that WARM_RESTART_TABLE|orchagent key was set after the first - # appDB port table operation - orchStateCount = 0 - for message in pubsubMessages: - print message - key = message['channel'].split(':', 1)[1] - print key - if message['data'] != 'hset' and message['data'] != 'del': - continue - if key.find(swsscommon.APP_PORT_TABLE_NAME)==0: - portOperStatusChanged = True - else: - # found one orchagent WARM_RESTART_TABLE operation after appDB port table change - if portOperStatusChanged == True: - orchStateCount += 1; - - # Only WARM_RESTART_TABLE|orchagent state=reconciled operation may exist after port oper status change. - assert orchStateCount == 1 - - #clean up arp - dvs.runcmd("arp -d 10.0.0.1") - dvs.runcmd("arp -d 10.0.0.3") - dvs.runcmd("arp -d 10.0.0.5") - - intf_tbl._del("Ethernet0|10.0.0.0/31") - intf_tbl._del("Ethernet4|10.0.0.2/31") - intf_tbl._del("Ethernet8|10.0.0.4/31") - intf_tbl._del("Ethernet0") - intf_tbl._del("Ethernet4") - intf_tbl._del("Ethernet8") - time.sleep(2) - - -############################################################################# -# # -# Routing Warm-Restart Testing # -# # -############################################################################# - - def set_restart_timer(dvs, db, app_name, value): create_entry_tbl( db, @@ -980,967 +195,1875 @@ def set_restart_timer(dvs, db, app_name, value): ] ) -################################################################################ -# -# Routing warm-restart testcases -# -################################################################################ - - -def test_routing_WarmRestart(dvs, testlog): +# 'ip neigh flush all' won't remove failed entries if number of neighs less than gc_threshold1 +# Also it takes time to remove them completly. +# We use arp off/on to do it +def flush_neigh_entries(dvs): + dvs.runcmd("ip link set group default arp off") + dvs.runcmd("ip link set group default arp on") - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) +# Add neighbor entries on servers connecting to SONiC ports +# ping them to get the neighbor entries +def setup_initial_neighbors(dvs): + for i in range(8, 8+NUM_INTF): + for j in range(NUM_NEIGH_PER_INTF): + dvs.servers[i].runcmd("ip addr add {}.0.0.{}/24 dev eth0".format(i*4, j+2)) + dvs.servers[i].runcmd("ip -6 addr add {}00::{}/64 dev eth0".format(i*4,j+2)) - # Restart-timer to utilize during the following testcases - restart_timer = 15 + time.sleep(1) + for i in range(8, 8+NUM_INTF): + for j in range(NUM_NEIGH_PER_INTF): + dvs.runcmd(['sh', '-c', "ping -c 1 -W 0 -q {}.0.0.{} > /dev/null 2>&1".format(i*4,j+2)]) + dvs.runcmd(['sh', '-c', "ping6 -c 1 -W 0 -q {}00::{} > /dev/null 2>&1".format(i*4,j+2)]) - ############################################################################# - # - # Baseline configuration - # - ############################################################################# +# Del half of the ips and a new half of the ips +# note: the first ipv4 can not be deleted only +def del_and_add_neighbors(dvs): + for i in range(8, 8+NUM_INTF): + for j in range(NUM_NEIGH_PER_INTF/2): + dvs.servers[i].runcmd("ip addr del {}.0.0.{}/24 dev eth0".format(i*4, j+NUM_NEIGH_PER_INTF/2+2)) + dvs.servers[i].runcmd("ip -6 addr del {}00::{}/64 dev eth0".format(i*4,j+NUM_NEIGH_PER_INTF/2+2)) + dvs.servers[i].runcmd("ip addr add {}.0.0.{}/24 dev eth0".format(i*4, j+NUM_NEIGH_PER_INTF+2)) + dvs.servers[i].runcmd("ip -6 addr add {}00::{}/64 dev eth0".format(i*4,j+NUM_NEIGH_PER_INTF+2)) +#ping new IPs +def ping_new_ips(dvs): + for i in range(8, 8+NUM_INTF): + for j in range(NUM_NEIGH_PER_INTF/2): + dvs.runcmd(['sh', '-c', "ping -c 1 -W 0 -q {}.0.0.{} > /dev/null 2>&1".format(i*4,j+NUM_NEIGH_PER_INTF+2)]) + dvs.runcmd(['sh', '-c', "ping6 -c 1 -W 0 -q {}00::{} > /dev/null 2>&1".format(i*4,j+NUM_NEIGH_PER_INTF+2)]) - # Defining create neighbor entries (4 ipv4 and 4 ip6, two each on each interface) in linux kernel - intfs = ["Ethernet0", "Ethernet4", "Ethernet8"] - - # Enable ipv6 on docker - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - - # Defining create neighbor entries (4 ipv4 and 4 ip6, two each on each interface) in linux kernel - intf_tbl = swsscommon.Table(conf_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - intf_tbl.set("{}|111.0.0.1/24".format(intfs[0]), fvs) - intf_tbl.set("{}|1110::1/64".format(intfs[0]), fvs) - intf_tbl.set("{}|122.0.0.1/24".format(intfs[1]), fvs) - intf_tbl.set("{}|1220::1/64".format(intfs[1]), fvs) - intf_tbl.set("{}|133.0.0.1/24".format(intfs[2]), fvs) - intf_tbl.set("{}|1330::1/64".format(intfs[2]), fvs) - intf_tbl.set("{}".format(intfs[0]), fvs) - intf_tbl.set("{}".format(intfs[0]), fvs) - intf_tbl.set("{}".format(intfs[1]), fvs) - intf_tbl.set("{}".format(intfs[1]), fvs) - intf_tbl.set("{}".format(intfs[2]), fvs) - intf_tbl.set("{}".format(intfs[2]), fvs) - dvs.runcmd("ip link set {} up".format(intfs[0])) - dvs.runcmd("ip link set {} up".format(intfs[1])) - dvs.runcmd("ip link set {} up".format(intfs[2])) +class TestWarmReboot(object): + def test_PortSyncdWarmRestart(self, dvs, testlog): - time.sleep(1) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - # - # Setting peer's ip-addresses and associated neighbor-entries - # - ips = ["111.0.0.2", "122.0.0.2", "133.0.0.2"] - v6ips = ["1110::2", "1220::2", "1330::2"] - macs = ["00:00:00:00:11:02", "00:00:00:00:12:02", "00:00:00:00:13:02"] + dvs.runcmd("config warm_restart enable swss") - for i in range(len(ips)): - dvs.runcmd("ip neigh add {} dev {} lladdr {}".format(ips[i], intfs[i%2], macs[i])) + dvs.runcmd("ifconfig Ethernet16 up") + dvs.runcmd("ifconfig Ethernet20 up") - for i in range(len(v6ips)): - dvs.runcmd("ip -6 neigh add {} dev {} lladdr {}".format(v6ips[i], intfs[i%2], macs[i])) + time.sleep(1) - time.sleep(1) + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet16|11.0.0.1/29", fvs) + intf_tbl.set("Ethernet20|11.0.0.9/29", fvs) + intf_tbl.set("Ethernet16", fvs) + intf_tbl.set("Ethernet20", fvs) + dvs.runcmd("ifconfig Ethernet16 up") + dvs.runcmd("ifconfig Ethernet20 up") - # - # Defining baseline IPv4 non-ecmp route-entries - # - dvs.runcmd("ip route add 192.168.1.100/32 nexthop via 111.0.0.2") - dvs.runcmd("ip route add 192.168.1.200/32 nexthop via 122.0.0.2") - dvs.runcmd("ip route add 192.168.1.230/32 nexthop via 133.0.0.2") + dvs.servers[4].runcmd("ip link set down dev eth0") == 0 + dvs.servers[4].runcmd("ip link set up dev eth0") == 0 + dvs.servers[4].runcmd("ifconfig eth0 11.0.0.2/29") + dvs.servers[4].runcmd("ip route add default via 11.0.0.1") - # - # Defining baseline IPv4 ecmp route-entries - # - dvs.runcmd("ip route add 192.168.1.1/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") - dvs.runcmd("ip route add 192.168.1.2/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") - dvs.runcmd("ip route add 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2") + dvs.servers[5].runcmd("ip link set down dev eth0") == 0 + dvs.servers[5].runcmd("ip link set up dev eth0") == 0 + dvs.servers[5].runcmd("ifconfig eth0 11.0.0.10/29") + dvs.servers[5].runcmd("ip route add default via 11.0.0.9") - # - # Defining baseline IPv6 non-ecmp route-entries - # - dvs.runcmd("ip -6 route add fc00:11:11::1/128 nexthop via 1110::2") - dvs.runcmd("ip -6 route add fc00:12:12::1/128 nexthop via 1220::2") - dvs.runcmd("ip -6 route add fc00:13:13::1/128 nexthop via 1330::2") + time.sleep(1) - # - # Defining baseline IPv6 ecmp route-entries - # - dvs.runcmd("ip -6 route add fc00:1:1::1/128 nexthop via 1110::2 nexthop via 1220::2 nexthop via 1330::2") - dvs.runcmd("ip -6 route add fc00:2:2::1/128 nexthop via 1110::2 nexthop via 1220::2 nexthop via 1330::2") - dvs.runcmd("ip -6 route add fc00:3:3::1/128 nexthop via 1110::2 nexthop via 1220::2") + # Ethernet port oper status should be up + check_port_oper_status(appl_db, "Ethernet16", "up") + check_port_oper_status(appl_db, "Ethernet20", "up") - time.sleep(5) + # Ping should work between servers via vs vlan interfaces + ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.10") + time.sleep(1) - # Enabling some extra logging for troubleshooting purposes - dvs.runcmd("swssloglevel -l INFO -c fpmsyncd") + neighTbl = swsscommon.Table(appl_db, "NEIGH_TABLE") + (status, fvs) = neighTbl.get("Ethernet16:11.0.0.2") + assert status == True - # Subscribe to pubsub channels for routing-state associated to swss and sairedis dbs - pubsubAppDB = dvs.SubscribeAppDbObject("ROUTE_TABLE") - pubsubAsicDB = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_ROUTE_ENTRY") + (status, fvs) = neighTbl.get("Ethernet20:11.0.0.10") + assert status == True + restore_count = swss_get_RestoreCount(dvs, state_db) - ############################################################################# - # - # Testcase 1. Having routing-warm-reboot disabled, restart zebra and verify - # that the traditional/cold-boot logic is followed. - # - ############################################################################# + # restart portsyncd + dvs.runcmd(['sh', '-c', 'pkill -x portsyncd']) - # Restart zebra - dvs.stop_zebra() - dvs.start_zebra() + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE") + dvs.runcmd(['sh', '-c', 'supervisorctl start portsyncd']) - time.sleep(5) + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == 0 - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "") + #new ip on server 5 + dvs.servers[5].runcmd("ifconfig eth0 11.0.0.11/29") - # Verify that multiple changes are seen in swss and sairedis logs as there's - # no warm-reboot logic in place. - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) != 0 + # Ping should work between servers via vs Ethernet interfaces + ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.11") - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) != 0 + # new neighbor learn on VS + (status, fvs) = neighTbl.get("Ethernet20:11.0.0.11") + assert status == True + # Port state change reflected in appDB correctly + dvs.servers[6].runcmd("ip link set down dev eth0") == 0 + dvs.servers[6].runcmd("ip link set up dev eth0") == 0 + time.sleep(1) + + check_port_oper_status(appl_db, "Ethernet16", "up") + check_port_oper_status(appl_db, "Ethernet20", "up") + check_port_oper_status(appl_db, "Ethernet24", "up") + + + swss_app_check_RestoreCount_single(state_db, restore_count, "portsyncd") + + intf_tbl._del("Ethernet16|11.0.0.1/29") + intf_tbl._del("Ethernet20|11.0.0.9/29") + intf_tbl._del("Ethernet16") + intf_tbl._del("Ethernet20") + time.sleep(2) + + + def test_VlanMgrdWarmRestart(self, dvs, testlog): + + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + dvs.runcmd("ifconfig Ethernet16 0") + dvs.runcmd("ifconfig Ethernet20 0") + + dvs.runcmd("ifconfig Ethernet16 up") + dvs.runcmd("ifconfig Ethernet20 up") + + time.sleep(1) + + dvs.runcmd("config warm_restart enable swss") + + # create vlan + create_entry_tbl( + conf_db, + "VLAN", "Vlan16", + [ + ("vlanid", "16"), + ] + ) + # create vlan + create_entry_tbl( + conf_db, + "VLAN", "Vlan20", + [ + ("vlanid", "20"), + ] + ) + # create vlan member entry in config db. Don't use Ethernet0/4/8/12 as IP configured on them in previous testing. + create_entry_tbl( + conf_db, + "VLAN_MEMBER", "Vlan16|Ethernet16", + [ + ("tagging_mode", "untagged"), + ] + ) + + create_entry_tbl( + conf_db, + "VLAN_MEMBER", "Vlan20|Ethernet20", + [ + ("tagging_mode", "untagged"), + ] + ) + + time.sleep(1) + + intf_tbl = swsscommon.Table(conf_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Vlan16|11.0.0.1/29", fvs) + intf_tbl.set("Vlan20|11.0.0.9/29", fvs) + intf_tbl.set("Vlan16", fvs) + intf_tbl.set("Vlan20", fvs) + dvs.runcmd("ifconfig Vlan16 up") + dvs.runcmd("ifconfig Vlan20 up") + + dvs.servers[4].runcmd("ifconfig eth0 11.0.0.2/29") + dvs.servers[4].runcmd("ip route add default via 11.0.0.1") + + dvs.servers[5].runcmd("ifconfig eth0 11.0.0.10/29") + dvs.servers[5].runcmd("ip route add default via 11.0.0.9") + + time.sleep(1) + + # Ping should work between servers via vs vlan interfaces + ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.10") + time.sleep(1) + + tbl = swsscommon.Table(appl_db, "NEIGH_TABLE") + (status, fvs) = tbl.get("Vlan16:11.0.0.2") + assert status == True - ############################################################################# - # - # Testcase 2. Restart zebra and make no control-plane changes. - # For this and all subsequent test-cases routing-warm-reboot - # feature will be kept enabled. - # - ############################################################################# + (status, fvs) = tbl.get("Vlan20:11.0.0.10") + assert status == True + (exitcode, bv_before) = dvs.runcmd("bridge vlan") + print(bv_before) - # Enabling bgp warmrestart and setting restart timer. - # The following two instructions will be substituted by the commented ones - # once the later ones are added to sonic-utilities repo. + restore_count = swss_get_RestoreCount(dvs, state_db) - dvs.runcmd("config warm_restart enable bgp") - dvs.runcmd("config warm_restart bgp_timer {}".format(restart_timer)) + dvs.runcmd(['sh', '-c', 'pkill -x vlanmgrd']) - time.sleep(1) + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE") - # Restart zebra - dvs.stop_zebra() - dvs.start_zebra() + dvs.runcmd(['sh', '-c', 'supervisorctl start vlanmgrd']) + time.sleep(2) - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + (exitcode, bv_after) = dvs.runcmd("bridge vlan") + assert bv_after == bv_before - # Verify swss changes -- none are expected this time - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 0 + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub, ignore=["SAI_OBJECT_TYPE_FDB_ENTRY"]) + assert nadd == 0 + assert ndel == 0 - # Verify swss changes -- none are expected this time - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 0 + #new ip on server 5 + dvs.servers[5].runcmd("ifconfig eth0 11.0.0.11/29") + # Ping should work between servers via vs vlan interfaces + ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.11") - ############################################################################# - # - # Testcase 3. Restart zebra and add one new non-ecmp IPv4 prefix - # - ############################################################################# + # new neighbor learn on VS + (status, fvs) = tbl.get("Vlan20:11.0.0.11") + assert status == True - # Stop zebra - dvs.stop_zebra() + swss_app_check_RestoreCount_single(state_db, restore_count, "vlanmgrd") - # Add new prefix - dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) + intf_tbl._del("Vlan16|11.0.0.1/29") + intf_tbl._del("Vlan20|11.0.0.9/29") + intf_tbl._del("Vlan16") + intf_tbl._del("Vlan20") + time.sleep(2) - # Start zebra - dvs.start_zebra() + def test_swss_neighbor_syncup(self, dvs, testlog): - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.100.0/24" - assert rt_val == {"ifname": "Ethernet0", "nexthop": "111.0.0.2"} + dvs.runcmd("config warm_restart enable swss") - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "192.168.100.0/24" + # + # Testcase1: + # Add neighbor entries in linux kernel, appDB should get all of them + # + # create neighbor entries (4 ipv4 and 4 ip6, two each on each interface) in linux kernel + intfs = ["Ethernet24", "Ethernet28"] - ############################################################################# - # - # Testcase 4. Restart zebra and withdraw one non-ecmp IPv4 prefix - # - ############################################################################# + for intf in intfs: + # set timeout to be the same as real HW + dvs.runcmd("sysctl -w net.ipv4.neigh.{}.base_reachable_time_ms=1800000".format(intf)) + dvs.runcmd("sysctl -w net.ipv6.neigh.{}.base_reachable_time_ms=1800000".format(intf)) + #enable ipv6 on docker + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") - # Stop zebra - dvs.stop_zebra() + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("{}|24.0.0.1/24".format(intfs[0]), fvs) + intf_tbl.set("{}|28.0.0.9/24".format(intfs[1]), fvs) + intf_tbl.set("{}|2400::1/64".format(intfs[0]), fvs) + intf_tbl.set("{}|2800::1/64".format(intfs[1]), fvs) + intf_tbl.set("{}".format(intfs[0]), fvs) + intf_tbl.set("{}".format(intfs[1]), fvs) + intf_tbl.set("{}".format(intfs[0]), fvs) + intf_tbl.set("{}".format(intfs[1]), fvs) + dvs.runcmd("ifconfig {} up".format(intfs[0])) + dvs.runcmd("ifconfig {} up".format(intfs[1])) - # Delete prefix - dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) + ips = ["24.0.0.2", "24.0.0.3", "28.0.0.2", "28.0.0.3"] + v6ips = ["2400::2", "2400::3", "2800::2", "2800::3"] - # Start zebra - dvs.start_zebra() + macs = ["00:00:00:00:24:02", "00:00:00:00:24:03", "00:00:00:00:28:02", "00:00:00:00:28:03"] - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + for i in range(len(ips)): + dvs.runcmd("ip neigh add {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], macs[i])) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key == "192.168.100.0/24" + for i in range(len(v6ips)): + dvs.runcmd("ip -6 neigh add {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], macs[i])) - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key['dest'] == "192.168.100.0/24" + time.sleep(1) + # Check the neighbor entries are inserted correctly + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, "NEIGH_TABLE") - ############################################################################# - # - # Testcase 5. Restart zebra and add a new IPv4 ecmp-prefix - # - ############################################################################# + for i in range(len(ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) + assert status == True + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv4" - # Stop zebra - dvs.stop_zebra() + for i in range(len(v6ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) + assert status == True - # Add prefix - dvs.runcmd("ip route add 192.168.200.0/24 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") - time.sleep(1) + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv6" - # Start zebra - dvs.start_zebra() + # + # Testcase 2: + # Restart neighsyncd without change neighbor entries, nothing should be sent to appDB or sairedis, + # appDB should be kept the same. + # + + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) + + # stop neighsyncd and sairedis.rec + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + marker = dvs.add_log_marker() + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + start_neighsyncd(dvs) + start_restore_neighbors(dvs) + time.sleep(10) - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Check the neighbor entries are still in appDB correctly + for i in range(len(ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) + assert status == True - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.200.0/24" - assert rt_val == {"ifname": "Ethernet0,Ethernet4,Ethernet8", "nexthop": "111.0.0.2,122.0.0.2,133.0.0.2"} + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv4" - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "192.168.200.0/24" + for i in range(len(v6ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) + assert status == True + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv6" - ############################################################################# - # - # Testcase 6. Restart zebra and delete one existing IPv4 ecmp-prefix. - # - ############################################################################# + # check syslog and sairedis.rec file for activities + check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == 0 + + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") + + # + # Testcase 3: + # stop neighsyncd, delete even nummber ipv4/ipv6 neighbor entries from each interface, warm start neighsyncd. + # the neighsyncd is supposed to sync up the entries from kernel after warm restart + # note: there was an issue for neighbor delete, it will be marked as FAILED instead of deleted in kernel + # but it will send netlink message to be removed from appDB, so it works ok here, + # just that if we want to add the same neighbor again, use "change" instead of "add" + + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) + + # stop neighsyncd + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + marker = dvs.add_log_marker() + + # delete even nummber of ipv4/ipv6 neighbor entries from each interface + for i in range(0, len(ips), 2): + dvs.runcmd("ip neigh del {} dev {}".format(ips[i], intfs[i/2])) + for i in range(0, len(v6ips), 2): + dvs.runcmd("ip -6 neigh del {} dev {}".format(v6ips[i], intfs[i/2])) - # Stop zebra - dvs.stop_zebra() + # start neighsyncd again + start_neighsyncd(dvs) + start_restore_neighbors(dvs) + time.sleep(10) - # Delete prefix - dvs.runcmd("ip route del 192.168.200.0/24 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") - time.sleep(1) + # check ipv4 and ipv6 neighbors + for i in range(len(ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) + #should not see deleted neighbor entries + if i % 2 == 0: + assert status == False + continue + else: + assert status == True + + #undeleted entries should still be there. + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv4" - # Start zebra - dvs.start_zebra() + for i in range(len(v6ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) + #should not see deleted neighbor entries + if i % 2 == 0: + assert status == False + continue + else: + assert status == True + + #undeleted entries should still be there. + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv6" - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # check syslog and sairedis.rec file for activities + # 2 deletes each for ipv4 and ipv6 + # 4 neighbor removal in asic db + check_syslog_for_neighbor_entry(dvs, marker, 0, 2, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 0, 2, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == 4 + + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") + + + # + # Testcase 4: + # Stop neighsyncd, add even nummber of ipv4/ipv6 neighbor entries to each interface again, + # Start neighsyncd + # The neighsyncd is supposed to sync up the entries from kernel after warm restart + # Check the timer is not retrieved from configDB since it is not configured + + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) + + # stop neighsyncd + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + marker = dvs.add_log_marker() + + # add even nummber of ipv4/ipv6 neighbor entries to each interface + # use "change" if neighbor is in FAILED state + for i in range(0, len(ips), 2): + (rc, output) = dvs.runcmd(['sh', '-c', "ip -4 neigh | grep {}".format(ips[i])]) + print output + if output: + dvs.runcmd("ip neigh change {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], macs[i])) + else: + dvs.runcmd("ip neigh add {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], macs[i])) + + for i in range(0, len(v6ips), 2): + (rc, output) = dvs.runcmd(['sh', '-c', "ip -6 neigh | grep {}".format(v6ips[i])]) + print output + if output: + dvs.runcmd("ip -6 neigh change {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], macs[i])) + else: + dvs.runcmd("ip -6 neigh add {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], macs[i])) + + # start neighsyncd again + start_neighsyncd(dvs) + start_restore_neighbors(dvs) + time.sleep(10) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key == "192.168.200.0/24" + # no neighsyncd timer configured + check_no_neighsyncd_timer(dvs) - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key['dest'] == "192.168.200.0/24" + # check ipv4 and ipv6 neighbors, should see all neighbors + for i in range(len(ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) + assert status == True + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv4" + for i in range(len(v6ips)): + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) + assert status == True + for v in fvs: + if v[0] == "neigh": + assert v[1] == macs[i] + if v[0] == "family": + assert v[1] == "IPv6" - ############################################################################# - # - # Testcase 7. Restart zebra and add one new path to an IPv4 ecmp-prefix - # - ############################################################################# + # check syslog and asic db for activities + # 2 news entries for ipv4 and ipv6 each + # 4 neighbor creation in asic db + check_syslog_for_neighbor_entry(dvs, marker, 2, 0, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 2, 0, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 4 + assert ndel == 0 + + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") + + # + # Testcase 5: + # Even number of ip4/6 neigbors updated with new mac. + # Odd number of ipv4/6 neighbors removed + # neighbor syncd should sync it up after warm restart + # include the timer settings in this testcase + + # setup timer in configDB + timer_value = "15" + + dvs.runcmd("config warm_restart neighsyncd_timer {}".format(timer_value)) + + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) + + # stop neighsyncd + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + marker = dvs.add_log_marker() + + # Even number of ip4/6 neigbors updated with new mac. + # Odd number of ipv4/6 neighbors removed + newmacs = ["00:00:00:01:12:02", "00:00:00:01:12:03", "00:00:00:01:16:02", "00:00:00:01:16:03"] + + for i in range(len(ips)): + if i % 2 == 0: + dvs.runcmd("ip neigh change {} dev {} lladdr {} nud reachable".format(ips[i], intfs[i/2], newmacs[i])) + else: + dvs.runcmd("ip neigh del {} dev {}".format(ips[i], intfs[i/2])) + + for i in range(len(v6ips)): + if i % 2 == 0: + dvs.runcmd("ip -6 neigh change {} dev {} lladdr {} nud reachable".format(v6ips[i], intfs[i/2], newmacs[i])) + else: + dvs.runcmd("ip -6 neigh del {} dev {}".format(v6ips[i], intfs[i/2])) + + # start neighsyncd again + start_neighsyncd(dvs) + start_restore_neighbors(dvs) + time.sleep(10) + # timer is not expired yet, state should be "restored" + swss_app_check_warmstart_state(state_db, "neighsyncd", "restored") + time.sleep(10) - # Stop zebra - dvs.stop_zebra() + # check neigh syncd timer is retrived from configDB + check_neighsyncd_timer(dvs, timer_value) + + # check ipv4 and ipv6 neighbors, should see all neighbors with updated info + for i in range(len(ips)): + if i % 2 == 0: + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) + assert status == True + for v in fvs: + if v[0] == "neigh": + assert v[1] == newmacs[i] + if v[0] == "family": + assert v[1] == "IPv4" + else: + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], ips[i])) + assert status == False + + for i in range(len(v6ips)): + if i % 2 == 0: + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) + assert status == True + for v in fvs: + if v[0] == "neigh": + assert v[1] == newmacs[i] + if v[0] == "family": + assert v[1] == "IPv6" + else: + (status, fvs) = tbl.get("{}:{}".format(intfs[i/2], v6ips[i])) + assert status == False + + time.sleep(2) + + # check syslog and asic db for activities + # 2 news, 2 deletes for ipv4 and ipv6 each + # 4 set, 4 removes for neighbor in asic db + check_syslog_for_neighbor_entry(dvs, marker, 2, 2, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 2, 2, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 4 + assert ndel == 4 + + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") + + # post-cleanup + dvs.runcmd("ip -s neigh flush all") + dvs.runcmd("ip -6 -s neigh flush all") + + intf_tbl._del("{}|24.0.0.1/24".format(intfs[0])) + intf_tbl._del("{}|28.0.0.9/24".format(intfs[1])) + intf_tbl._del("{}|2400::1/64".format(intfs[0])) + intf_tbl._del("{}|2800::1/64".format(intfs[1])) + intf_tbl._del("{}".format(intfs[0])) + intf_tbl._del("{}".format(intfs[1])) + intf_tbl._del("{}".format(intfs[0])) + intf_tbl._del("{}".format(intfs[1])) + time.sleep(2) + + + # TODO: The condition of warm restart readiness check is still under discussion. + def test_OrchagentWarmRestartReadyCheck(self, dvs, testlog): + + time.sleep(1) + + dvs.runcmd("config warm_restart enable swss") + + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") + + dvs.servers[0].runcmd("ifconfig eth0 10.0.0.1/31") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + + dvs.servers[1].runcmd("ifconfig eth0 10.0.0.3/31") + dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + + + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(appl_db, swsscommon.APP_ROUTE_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) + + ps.set("2.2.2.0/24", fvs) + + time.sleep(1) + # Should fail, since neighbor for next 10.0.0.1 has not been not resolved yet + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK failed\n" + + # Should succeed, the option for skipPendingTaskCheck -s and noFreeze -n have been provided. + # Wait up to 500 milliseconds for response from orchagent. Default wait time is 1000 milliseconds. + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check -n -s -w 500") + assert result == "RESTARTCHECK succeeded\n" + + # get neighbor and arp entry + dvs.servers[1].runcmd("ping -c 1 10.0.0.1") + + time.sleep(1) + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK succeeded\n" + + # Should fail since orchagent has been frozen at last step. + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check -n -s -w 500") + assert result == "RESTARTCHECK failed\n" + + # Cleaning previously pushed route-entry to ease life of subsequent testcases. + ps._del("2.2.2.0/24") + time.sleep(1) + + intf_tbl._del("Ethernet0|10.0.0.0/31") + intf_tbl._del("Ethernet4|10.0.0.2/31") + intf_tbl._del("Ethernet0") + intf_tbl._del("Ethernet4") + time.sleep(2) + + # recover for test cases after this one. + dvs.stop_swss() + dvs.start_swss() + time.sleep(5) + + def test_swss_port_state_syncup(self, dvs, testlog): - # Add new path - dvs.runcmd("ip route del 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2") - dvs.runcmd("ip route add 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") - time.sleep(1) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - # Start zebra - dvs.start_zebra() + dvs.runcmd("config warm_restart enable swss") + + tbl = swsscommon.Table(appl_db, swsscommon.APP_PORT_TABLE_NAME) - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + restore_count = swss_get_RestoreCount(dvs, state_db) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.1.3" - assert rt_val == {"ifname": "Ethernet0,Ethernet4,Ethernet8", "nexthop": "111.0.0.2,122.0.0.2,133.0.0.2"} + # update port admin state + intf_tbl = swsscommon.Table(conf_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + intf_tbl.set("Ethernet8|10.0.0.4/31", fvs) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + intf_tbl.set("Ethernet8", fvs) + dvs.runcmd("ifconfig Ethernet0 up") + dvs.runcmd("ifconfig Ethernet4 up") + dvs.runcmd("ifconfig Ethernet8 up") - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "192.168.1.3/32" + dvs.runcmd("arp -s 10.0.0.1 00:00:00:00:00:01") + dvs.runcmd("arp -s 10.0.0.3 00:00:00:00:00:02") + dvs.runcmd("arp -s 10.0.0.5 00:00:00:00:00:03") + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[1].runcmd("ip link set down dev eth0") == 0 + dvs.servers[2].runcmd("ip link set down dev eth0") == 0 - ############################################################################# - # - # Testcase 8. Restart zebra and delete one ecmp-path from an IPv4 ecmp-prefix. - # - ############################################################################# + dvs.servers[2].runcmd("ip link set up dev eth0") == 0 + time.sleep(3) - # Stop zebra - dvs.stop_zebra() + for i in [0, 1, 2]: + (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) + assert status == True + oper_status = "unknown" + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break + if i == 2: + assert oper_status == "up" + else: + assert oper_status == "down" + + intf_tbl._del("Ethernet0|10.0.0.0/31") + intf_tbl._del("Ethernet4|10.0.0.2/31") + intf_tbl._del("Ethernet8|10.0.0.4/31") + intf_tbl._del("Ethernet0") + intf_tbl._del("Ethernet4") + intf_tbl._del("Ethernet8") + time.sleep(2) + + dvs.stop_swss() + time.sleep(3) + + # flap the port oper status for Ethernet0, Ethernet4 and Ethernet8 + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[1].runcmd("ip link set down dev eth0") == 0 + dvs.servers[2].runcmd("ip link set down dev eth0") == 0 + + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[1].runcmd("ip link set up dev eth0") == 0 + + time.sleep(5) + dbobjs =[(swsscommon.APPL_DB, swsscommon.APP_PORT_TABLE_NAME + ":*"), \ + (swsscommon.STATE_DB, swsscommon.STATE_WARM_RESTART_TABLE_NAME + "|orchagent")] + pubsubDbs = dvs.SubscribeDbObjects(dbobjs) + dvs.start_swss() + start_restore_neighbors(dvs) + time.sleep(10) - # Delete ecmp-path - dvs.runcmd("ip route del 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") - dvs.runcmd("ip route add 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2") - time.sleep(1) + swss_check_RestoreCount(dvs, state_db, restore_count) - # Start zebra - dvs.start_zebra() + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) + intf_tbl.set("Ethernet8|10.0.0.4/31", fvs) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet4", fvs) + intf_tbl.set("Ethernet8", fvs) + time.sleep(3) - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + for i in [0, 1, 2]: + (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) + assert status == True + oper_status = "unknown" + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break + if i == 2: + assert oper_status == "down" + else: + assert oper_status == "up" + + # check the pubsub messages. + # No appDB port table operation should exist before orchagent state restored flag got set. + # appDB port table status sync up happens before WARM_RESTART_TABLE reconciled flag is set + # pubsubMessages is an ordered list of pubsub messages. + pubsubMessages = dvs.GetSubscribedMessages(pubsubDbs) + + portOperStatusChanged = False + # number of times that WARM_RESTART_TABLE|orchagent key was set after the first + # appDB port table operation + orchStateCount = 0 + for message in pubsubMessages: + print message + key = message['channel'].split(':', 1)[1] + print key + if message['data'] != 'hset' and message['data'] != 'del': + continue + if key.find(swsscommon.APP_PORT_TABLE_NAME)==0: + portOperStatusChanged = True + else: + # found one orchagent WARM_RESTART_TABLE operation after appDB port table change + if portOperStatusChanged == True: + orchStateCount += 1; + + # Only WARM_RESTART_TABLE|orchagent state=reconciled operation may exist after port oper status change. + assert orchStateCount == 1 + + #clean up arp + dvs.runcmd("arp -d 10.0.0.1") + dvs.runcmd("arp -d 10.0.0.3") + dvs.runcmd("arp -d 10.0.0.5") + + intf_tbl._del("Ethernet0|10.0.0.0/31") + intf_tbl._del("Ethernet4|10.0.0.2/31") + intf_tbl._del("Ethernet8|10.0.0.4/31") + intf_tbl._del("Ethernet0") + intf_tbl._del("Ethernet4") + intf_tbl._del("Ethernet8") + time.sleep(2) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.1.3" - assert rt_val == {"ifname": "Ethernet0,Ethernet4", "nexthop": "111.0.0.2,122.0.0.2"} - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "192.168.1.3/32" + ############################################################################# + # # + # Routing Warm-Restart Testing # + # # + ############################################################################# - ############################################################################# + ################################################################################ # - # Testcase 9. Restart zebra and add one new non-ecmp IPv6 prefix + # Routing warm-restart testcases # - ############################################################################# + ################################################################################ + + + def test_routing_WarmRestart(self, dvs, testlog): + + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + # Restart-timer to utilize during the following testcases + restart_timer = 15 + + + ############################################################################# + # + # Baseline configuration + # + ############################################################################# + + + # Defining create neighbor entries (4 ipv4 and 4 ip6, two each on each interface) in linux kernel + intfs = ["Ethernet0", "Ethernet4", "Ethernet8"] + + # Enable ipv6 on docker + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + # Defining create neighbor entries (4 ipv4 and 4 ip6, two each on each interface) in linux kernel + intf_tbl = swsscommon.Table(conf_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("{}|111.0.0.1/24".format(intfs[0]), fvs) + intf_tbl.set("{}|1110::1/64".format(intfs[0]), fvs) + intf_tbl.set("{}|122.0.0.1/24".format(intfs[1]), fvs) + intf_tbl.set("{}|1220::1/64".format(intfs[1]), fvs) + intf_tbl.set("{}|133.0.0.1/24".format(intfs[2]), fvs) + intf_tbl.set("{}|1330::1/64".format(intfs[2]), fvs) + intf_tbl.set("{}".format(intfs[0]), fvs) + intf_tbl.set("{}".format(intfs[0]), fvs) + intf_tbl.set("{}".format(intfs[1]), fvs) + intf_tbl.set("{}".format(intfs[1]), fvs) + intf_tbl.set("{}".format(intfs[2]), fvs) + intf_tbl.set("{}".format(intfs[2]), fvs) + dvs.runcmd("ip link set {} up".format(intfs[0])) + dvs.runcmd("ip link set {} up".format(intfs[1])) + dvs.runcmd("ip link set {} up".format(intfs[2])) - # Stop zebra - dvs.stop_zebra() + time.sleep(1) - # Add prefix - dvs.runcmd("ip -6 route add fc00:4:4::1/128 nexthop via 1110::2") - time.sleep(1) + # + # Setting peer's ip-addresses and associated neighbor-entries + # + ips = ["111.0.0.2", "122.0.0.2", "133.0.0.2"] + v6ips = ["1110::2", "1220::2", "1330::2"] + macs = ["00:00:00:00:11:02", "00:00:00:00:12:02", "00:00:00:00:13:02"] - # Start zebra - dvs.start_zebra() + for i in range(len(ips)): + dvs.runcmd("ip neigh add {} dev {} lladdr {}".format(ips[i], intfs[i%2], macs[i])) - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + for i in range(len(v6ips)): + dvs.runcmd("ip -6 neigh add {} dev {} lladdr {}".format(v6ips[i], intfs[i%2], macs[i])) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "fc00:4:4::1" - assert rt_val == {"ifname": "Ethernet0", "nexthop": "1110::2"} + time.sleep(1) - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "fc00:4:4::1/128" + # + # Defining baseline IPv4 non-ecmp route-entries + # + dvs.runcmd("ip route add 192.168.1.100/32 nexthop via 111.0.0.2") + dvs.runcmd("ip route add 192.168.1.200/32 nexthop via 122.0.0.2") + dvs.runcmd("ip route add 192.168.1.230/32 nexthop via 133.0.0.2") + # + # Defining baseline IPv4 ecmp route-entries + # + dvs.runcmd("ip route add 192.168.1.1/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") + dvs.runcmd("ip route add 192.168.1.2/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") + dvs.runcmd("ip route add 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2") - ############################################################################# - # - # Testcase 10. Restart zebra and withdraw one non-ecmp IPv6 prefix - # - ############################################################################# + # + # Defining baseline IPv6 non-ecmp route-entries + # + dvs.runcmd("ip -6 route add fc00:11:11::1/128 nexthop via 1110::2") + dvs.runcmd("ip -6 route add fc00:12:12::1/128 nexthop via 1220::2") + dvs.runcmd("ip -6 route add fc00:13:13::1/128 nexthop via 1330::2") - # Stop zebra - dvs.stop_zebra() + # + # Defining baseline IPv6 ecmp route-entries + # + dvs.runcmd("ip -6 route add fc00:1:1::1/128 nexthop via 1110::2 nexthop via 1220::2 nexthop via 1330::2") + dvs.runcmd("ip -6 route add fc00:2:2::1/128 nexthop via 1110::2 nexthop via 1220::2 nexthop via 1330::2") + dvs.runcmd("ip -6 route add fc00:3:3::1/128 nexthop via 1110::2 nexthop via 1220::2") - # Delete prefix - dvs.runcmd("ip -6 route del fc00:4:4::1/128 nexthop via 1110::2") - time.sleep(1) + time.sleep(5) + + # Enabling some extra logging for troubleshooting purposes + dvs.runcmd("swssloglevel -l INFO -c fpmsyncd") + + # Subscribe to pubsub channels for routing-state associated to swss and sairedis dbs + pubsubAppDB = dvs.SubscribeAppDbObject("ROUTE_TABLE") + pubsubAsicDB = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_ROUTE_ENTRY") - # Start zebra - dvs.start_zebra() - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + ############################################################################# + # + # Testcase 1. Having routing-warm-reboot disabled, restart zebra and verify + # that the traditional/cold-boot logic is followed. + # + ############################################################################# + + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() + + time.sleep(5) + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "") + + # Verify that multiple changes are seen in swss and sairedis logs as there's + # no warm-reboot logic in place. + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) != 0 + + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) != 0 + + + ############################################################################# + # + # Testcase 2. Restart zebra and make no control-plane changes. + # For this and all subsequent test-cases routing-warm-reboot + # feature will be kept enabled. + # + ############################################################################# + + + # Enabling bgp warmrestart and setting restart timer. + # The following two instructions will be substituted by the commented ones + # once the later ones are added to sonic-utilities repo. + + dvs.runcmd("config warm_restart enable bgp") + dvs.runcmd("config warm_restart bgp_timer {}".format(restart_timer)) + + time.sleep(1) - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key == "fc00:4:4::1" + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key['dest'] == "fc00:4:4::1/128" + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 0 - ############################################################################# - # - # Testcase 11. Restart fpmsyncd and make no control-plane changes. - # - ############################################################################# + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 0 - # Stop fpmsyncd - dvs.stop_fpmsyncd() + ############################################################################# + # + # Testcase 3. Restart zebra and add one new non-ecmp IPv4 prefix + # + ############################################################################# + + # Stop zebra + dvs.stop_zebra() + + # Add new prefix + dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) - # Start fpmsyncd - dvs.start_fpmsyncd() + # Start zebra + dvs.start_zebra() - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - # Verify swss changes -- none are expected this time - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 0 + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "192.168.100.0/24" + assert rt_val == {"ifname": "Ethernet0", "nexthop": "111.0.0.2"} - # Verify sairedis changes -- none are expected this time - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 0 + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "192.168.100.0/24" - ############################################################################# - # - # Testcase 12. Restart fpmsyncd and add one new non-ecmp IPv4 prefix - # - ############################################################################# + ############################################################################# + # + # Testcase 4. Restart zebra and withdraw one non-ecmp IPv4 prefix + # + ############################################################################# - # Stop fpmsyncd - dvs.stop_fpmsyncd() + # Stop zebra + dvs.stop_zebra() - # Add new prefix - dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) + # Delete prefix + dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) - # Start fpmsyncd - dvs.start_fpmsyncd() + # Start zebra + dvs.start_zebra() - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.100.0/24" - assert rt_val == {"ifname": "Ethernet0", "nexthop": "111.0.0.2"} + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key == "192.168.100.0/24" - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "192.168.100.0/24" + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key['dest'] == "192.168.100.0/24" - ############################################################################# - # - # Testcase 13. Restart fpmsyncd and withdraw one non-ecmp IPv4 prefix - # - ############################################################################# + ############################################################################# + # + # Testcase 5. Restart zebra and add a new IPv4 ecmp-prefix + # + ############################################################################# - # Stop fpmsyncd - dvs.stop_fpmsyncd() + # Stop zebra + dvs.stop_zebra() + + # Add prefix + dvs.runcmd("ip route add 192.168.200.0/24 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") + time.sleep(1) + + # Start zebra + dvs.start_zebra() - # Delete prefix - dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - # Start fpmsyncd - dvs.start_fpmsyncd() + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "192.168.200.0/24" + assert rt_val == {"ifname": "Ethernet0,Ethernet4,Ethernet8", "nexthop": "111.0.0.2,122.0.0.2,133.0.0.2"} + + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "192.168.200.0/24" + + + ############################################################################# + # + # Testcase 6. Restart zebra and delete one existing IPv4 ecmp-prefix. + # + ############################################################################# + + + # Stop zebra + dvs.stop_zebra() + + # Delete prefix + dvs.runcmd("ip route del 192.168.200.0/24 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") + time.sleep(1) + + # Start zebra + dvs.start_zebra() + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key == "192.168.200.0/24" - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key == "192.168.100.0/24" + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key['dest'] == "192.168.200.0/24" - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 1 - rt_key = json.loads(delobjs[0]['key']) - assert rt_key['dest'] == "192.168.100.0/24" + ############################################################################# + # + # Testcase 7. Restart zebra and add one new path to an IPv4 ecmp-prefix + # + ############################################################################# + + + # Stop zebra + dvs.stop_zebra() + + # Add new path + dvs.runcmd("ip route del 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2") + dvs.runcmd("ip route add 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") + time.sleep(1) + + # Start zebra + dvs.start_zebra() + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "192.168.1.3" + assert rt_val == {"ifname": "Ethernet0,Ethernet4,Ethernet8", "nexthop": "111.0.0.2,122.0.0.2,133.0.0.2"} + + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "192.168.1.3/32" + + + ############################################################################# + # + # Testcase 8. Restart zebra and delete one ecmp-path from an IPv4 ecmp-prefix. + # + ############################################################################# + + + # Stop zebra + dvs.stop_zebra() + + # Delete ecmp-path + dvs.runcmd("ip route del 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2 nexthop via 133.0.0.2") + dvs.runcmd("ip route add 192.168.1.3/32 nexthop via 111.0.0.2 nexthop via 122.0.0.2") + time.sleep(1) + + # Start zebra + dvs.start_zebra() + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - ############################################################################# - # - # Testcase 14. Restart zebra and add/remove a new non-ecmp IPv4 prefix. As - # the 'delete' instruction would arrive after the 'add' one, no - # changes should be pushed down to SwSS. - # - ############################################################################# + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "192.168.1.3" + assert rt_val == {"ifname": "Ethernet0,Ethernet4", "nexthop": "111.0.0.2,122.0.0.2"} + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "192.168.1.3/32" - # Restart zebra - dvs.stop_zebra() - dvs.start_zebra() - # Add/delete new prefix - dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) - dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) + ############################################################################# + # + # Testcase 9. Restart zebra and add one new non-ecmp IPv6 prefix + # + ############################################################################# + + + # Stop zebra + dvs.stop_zebra() - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Add prefix + dvs.runcmd("ip -6 route add fc00:4:4::1/128 nexthop via 1110::2") + time.sleep(1) - # Verify swss changes -- none are expected this time - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 0 and len(delobjs) == 0 + # Start zebra + dvs.start_zebra() - # Verify swss changes -- none are expected this time - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 0 and len(delobjs) == 0 + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "fc00:4:4::1" + assert rt_val == {"ifname": "Ethernet0", "nexthop": "1110::2"} - ############################################################################# - # - # Testcase 15. Restart zebra and generate an add/remove/add for new non-ecmp - # IPv4 prefix. Verify that only the second 'add' instruction is - # honored and the corresponding update passed down to SwSS. - # - ############################################################################# + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "fc00:4:4::1/128" + + + ############################################################################# + # + # Testcase 10. Restart zebra and withdraw one non-ecmp IPv6 prefix + # + ############################################################################# + + # Stop zebra + dvs.stop_zebra() + # Delete prefix + dvs.runcmd("ip -6 route del fc00:4:4::1/128 nexthop via 1110::2") + time.sleep(1) - # Restart zebra - dvs.stop_zebra() - dvs.start_zebra() + # Start zebra + dvs.start_zebra() - marker1 = dvs.add_log_marker("/var/log/swss/swss.rec") - marker2 = dvs.add_log_marker("/var/log/swss/sairedis.rec") + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - # Add/delete new prefix - dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) - dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") - time.sleep(1) - dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 122.0.0.2") - time.sleep(1) + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key == "fc00:4:4::1" - # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "restored") - time.sleep(restart_timer + 1) - swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - - # Verify the changed prefix is seen in swss - (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - rt_val = json.loads(addobjs[0]['vals']) - assert rt_key == "192.168.100.0/24" - assert rt_val == {"ifname": "Ethernet4", "nexthop": "122.0.0.2"} - - # Verify the changed prefix is seen in sairedis - (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) - assert len(addobjs) == 1 and len(delobjs) == 0 - rt_key = json.loads(addobjs[0]['key']) - assert rt_key['dest'] == "192.168.100.0/24" - - intf_tbl._del("{}|111.0.0.1/24".format(intfs[0])) - intf_tbl._del("{}|1110::1/64".format(intfs[0])) - intf_tbl._del("{}|122.0.0.1/24".format(intfs[1])) - intf_tbl._del("{}|1220::1/64".format(intfs[1])) - intf_tbl._del("{}|133.0.0.1/24".format(intfs[2])) - intf_tbl._del("{}|1330::1/64".format(intfs[2])) - intf_tbl._del("{}".format(intfs[0])) - intf_tbl._del("{}".format(intfs[0])) - intf_tbl._del("{}".format(intfs[1])) - intf_tbl._del("{}".format(intfs[1])) - intf_tbl._del("{}".format(intfs[2])) - intf_tbl._del("{}".format(intfs[2])) - time.sleep(2) + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key['dest'] == "fc00:4:4::1/128" -# macros for number of interfaces and number of neighbors -# TBD: NUM_NEIGH_PER_INTF >= 128 ips will cause test framework to hang by default kernel settings -# TBD: Need tune gc_thresh1/2/3 at host side of vs docker to support this. -NUM_INTF = 8 -NUM_NEIGH_PER_INTF = 16 #128 -NUM_OF_NEIGHS = (NUM_INTF*NUM_NEIGH_PER_INTF) + ############################################################################# + # + # Testcase 11. Restart fpmsyncd and make no control-plane changes. + # + ############################################################################# + + + # Stop fpmsyncd + dvs.stop_fpmsyncd() + + # Start fpmsyncd + dvs.start_fpmsyncd() -# 'ip neigh flush all' won't remove failed entries if number of neighs less than gc_threshold1 -# Also it takes time to remove them completly. -# We use arp off/on to do it -def flush_neigh_entries(dvs): - dvs.runcmd("ip link set group default arp off") - dvs.runcmd("ip link set group default arp on") + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") -# Add neighbor entries on servers connecting to SONiC ports -# ping them to get the neighbor entries -def setup_initial_neighbors(dvs): - for i in range(8, 8+NUM_INTF): - for j in range(NUM_NEIGH_PER_INTF): - dvs.servers[i].runcmd("ip addr add {}.0.0.{}/24 dev eth0".format(i*4, j+2)) - dvs.servers[i].runcmd("ip -6 addr add {}00::{}/64 dev eth0".format(i*4,j+2)) + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 0 - time.sleep(1) + # Verify sairedis changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 0 - for i in range(8, 8+NUM_INTF): - for j in range(NUM_NEIGH_PER_INTF): - dvs.runcmd(['sh', '-c', "ping -c 1 -W 0 -q {}.0.0.{} > /dev/null 2>&1".format(i*4,j+2)]) - dvs.runcmd(['sh', '-c', "ping6 -c 1 -W 0 -q {}00::{} > /dev/null 2>&1".format(i*4,j+2)]) -# Del half of the ips and a new half of the ips -# note: the first ipv4 can not be deleted only -def del_and_add_neighbors(dvs): - for i in range(8, 8+NUM_INTF): - for j in range(NUM_NEIGH_PER_INTF/2): - dvs.servers[i].runcmd("ip addr del {}.0.0.{}/24 dev eth0".format(i*4, j+NUM_NEIGH_PER_INTF/2+2)) - dvs.servers[i].runcmd("ip -6 addr del {}00::{}/64 dev eth0".format(i*4,j+NUM_NEIGH_PER_INTF/2+2)) - dvs.servers[i].runcmd("ip addr add {}.0.0.{}/24 dev eth0".format(i*4, j+NUM_NEIGH_PER_INTF+2)) - dvs.servers[i].runcmd("ip -6 addr add {}00::{}/64 dev eth0".format(i*4,j+NUM_NEIGH_PER_INTF+2)) + ############################################################################# + # + # Testcase 12. Restart fpmsyncd and add one new non-ecmp IPv4 prefix + # + ############################################################################# -#ping new IPs -def ping_new_ips(dvs): - for i in range(8, 8+NUM_INTF): - for j in range(NUM_NEIGH_PER_INTF/2): - dvs.runcmd(['sh', '-c', "ping -c 1 -W 0 -q {}.0.0.{} > /dev/null 2>&1".format(i*4,j+NUM_NEIGH_PER_INTF+2)]) - dvs.runcmd(['sh', '-c', "ping6 -c 1 -W 0 -q {}00::{} > /dev/null 2>&1".format(i*4,j+NUM_NEIGH_PER_INTF+2)]) -def test_system_warmreboot_neighbor_syncup(dvs, testlog): + # Stop fpmsyncd + dvs.stop_fpmsyncd() + + # Add new prefix + dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + # Start fpmsyncd + dvs.start_fpmsyncd() + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") - #enable ipv6 on docker - dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "192.168.100.0/24" + assert rt_val == {"ifname": "Ethernet0", "nexthop": "111.0.0.2"} - # flush all neighs first - flush_neigh_entries(dvs) - time.sleep(5) + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "192.168.100.0/24" - dvs.runcmd("config warm_restart enable system") - # Test neighbors on NUM_INTF (e,g 8) interfaces - # Ethernet32/36/.../60, with ip: 32.0.0.1/24... 60.0.0.1/24 - # ipv6: 3200::1/64...6000::1/64 - # bring up the servers'interfaces and assign NUM_NEIGH_PER_INTF (e,g 128) ips per interface - macs = [] - intf_tbl = swsscommon.Table(conf_db, "INTERFACE") - fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) - for i in range(8, 8+NUM_INTF): - # set timeout to be the same as real HW - # set stale timer bigger to avoid testbed difference related timing issues. - # set ip on server facing interfaces - # bring servers' interface up, save the macs - dvs.runcmd("sysctl -w net.ipv4.neigh.Ethernet{}.base_reachable_time_ms=1800000".format(i*4)) - dvs.runcmd("sysctl -w net.ipv6.neigh.Ethernet{}.base_reachable_time_ms=1800000".format(i*4)) - dvs.runcmd("sysctl -w net.ipv4.neigh.Ethernet{}.gc_stale_time=180".format(i*4)) - dvs.runcmd("sysctl -w net.ipv6.neigh.Ethernet{}.gc_stale_time=180".format(i*4)) - dvs.runcmd("ip addr flush dev Ethernet{}".format(i*4)) - intf_tbl.set("Ethernet{}|{}.0.0.1/24".format(i*4, i*4), fvs) - intf_tbl.set("Ethernet{}|{}00::1/64".format(i*4, i*4), fvs) - intf_tbl.set("Ethernet{}".format(i*4, i*4), fvs) - intf_tbl.set("Ethernet{}".format(i*4, i*4), fvs) - dvs.runcmd("ip link set Ethernet{} up".format(i*4, i*4)) - dvs.servers[i].runcmd("ip link set up dev eth0") - dvs.servers[i].runcmd("ip addr flush dev eth0") - #result = dvs.servers[i].runcmd_output("ifconfig eth0 | grep HWaddr | awk '{print $NF}'") - result = dvs.servers[i].runcmd_output("cat /sys/class/net/eth0/address") - macs.append(result.strip()) + ############################################################################# + # + # Testcase 13. Restart fpmsyncd and withdraw one non-ecmp IPv4 prefix + # + ############################################################################# + + + # Stop fpmsyncd + dvs.stop_fpmsyncd() + + # Delete prefix + dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) + + # Start fpmsyncd + dvs.start_fpmsyncd() + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key == "192.168.100.0/24" + + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 1 + rt_key = json.loads(delobjs[0]['key']) + assert rt_key['dest'] == "192.168.100.0/24" + - # - # Testcase 1: - # Setup initial neigbors - setup_initial_neighbors(dvs) + ############################################################################# + # + # Testcase 14. Restart zebra and add/remove a new non-ecmp IPv4 prefix. As + # the 'delete' instruction would arrive after the 'add' one, no + # changes should be pushed down to SwSS. + # + ############################################################################# + + + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() + + # Add/delete new prefix + dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) + dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 0 + + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 0 + + + ############################################################################# + # + # Testcase 15. Restart zebra and generate an add/remove/add for new non-ecmp + # IPv4 prefix. Verify that only the second 'add' instruction is + # honored and the corresponding update passed down to SwSS. + # + ############################################################################# + + + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() + + marker1 = dvs.add_log_marker("/var/log/swss/swss.rec") + marker2 = dvs.add_log_marker("/var/log/swss/sairedis.rec") + + # Add/delete new prefix + dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) + dvs.runcmd("ip route del 192.168.100.0/24 nexthop via 111.0.0.2") + time.sleep(1) + dvs.runcmd("ip route add 192.168.100.0/24 nexthop via 122.0.0.2") + time.sleep(1) + + # Verify FSM + swss_app_check_warmstart_state(state_db, "bgp", "restored") + time.sleep(restart_timer + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + # Verify the changed prefix is seen in swss + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + rt_val = json.loads(addobjs[0]['vals']) + assert rt_key == "192.168.100.0/24" + assert rt_val == {"ifname": "Ethernet4", "nexthop": "122.0.0.2"} + + # Verify the changed prefix is seen in sairedis + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 1 and len(delobjs) == 0 + rt_key = json.loads(addobjs[0]['key']) + assert rt_key['dest'] == "192.168.100.0/24" + + + ############################################################################# + # + # Testcase 16. Restart zebra and make no control-plane changes. + # Set WARM_RESTART_TABLE|IPv4|eoiu + # WARM_RESTART_TABLE|IPv6|eoiu + # Check route reconciliation wait time is reduced + # For this and all subsequent test-cases routing-warm-reboot + # feature will be kept enabled. + # + ############################################################################# + + + time.sleep(1) + # Hold time from EOIU detected for both Ipv4/Ipv6 to start route reconciliation + DEFAULT_EOIU_HOLD_INTERVAL = 3 + + # change to 20 for easy timeline check + restart_timer = 20 + + # clean up as that in bgp_eoiu_marker.py + del_entry_tbl(state_db, "BGP_STATE_TABLE", "IPv4|eoiu") + del_entry_tbl(state_db, "BGP_STATE_TABLE", "IPv6|eoiu") + + dvs.runcmd("config warm_restart bgp_timer {}".format(restart_timer)) + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() + + # + # Verify FSM: no eoiu, just default warm restart timer + # + swss_app_check_warmstart_state(state_db, "bgp", "restored") + # Periodic eoiu check timer, first wait 5 seconds, then check every 1 second + # DEFAULT_EOIU_HOLD_INTERVAL is 3 seconds. + # Since no EOIU set, after 3+ 5 + 1 seconds, the state still in restored state + time.sleep(DEFAULT_EOIU_HOLD_INTERVAL + 5 +1) + swss_app_check_warmstart_state(state_db, "bgp", "restored") + # default restart timer kicks in: + time.sleep(restart_timer - DEFAULT_EOIU_HOLD_INTERVAL -5) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + + time.sleep(1) + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() + + # + # Verify FSM: eoiu works as expected + # + swss_app_check_warmstart_state(state_db, "bgp", "restored") + # Set BGP_STATE_TABLE|Ipv4|eoiu BGP_STATE_TABLE|IPv6|eoiu + create_entry_tbl( + state_db, + "BGP_STATE_TABLE", "IPv4|eoiu", + [ + ("state", "reached"), + ("timestamp", "2019-04-25 09:39:19"), + ] + ) + create_entry_tbl( + state_db, + "BGP_STATE_TABLE", "IPv6|eoiu", + [ + ("state", "reached"), + ("timestamp", "2019-04-25 09:39:22"), + ] + ) + + # after DEFAULT_EOIU_HOLD_INTERVAL + inital eoiu check timer wait time + 1 seconds: 3+5+1 + # verify that bgp reached reconciled state + time.sleep(DEFAULT_EOIU_HOLD_INTERVAL + 5 + 1) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 0 + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 0 + + + del_entry_tbl(state_db, "BGP_STATE_TABLE", "IPv4|eoiu") + del_entry_tbl(state_db, "BGP_STATE_TABLE", "IPv6|eoiu") + time.sleep(1) + # Restart zebra + dvs.stop_zebra() + dvs.start_zebra() + + # + # Verify FSM: partial eoiu, fallback to default warm restart timer + # + swss_app_check_warmstart_state(state_db, "bgp", "restored") + # Set BGP_STATE_TABLE|Ipv4|eoiu but not BGP_STATE_TABLE|IPv6|eoiu + create_entry_tbl( + state_db, + "BGP_STATE_TABLE", "IPv4|eoiu", + [ + ("state", "reached"), + ("timestamp", "2019-04-25 09:39:19"), + ] + ) + + # Periodic eoiu check timer, first wait 5 seconds, then check every 1 second + # DEFAULT_EOIU_HOLD_INTERVAL is 3 seconds. + # Current bgp eoiu needs flag set on both Ipv4/Ipv6 to work, after 3+ 5 + 1 seconds, the state still in restored state + time.sleep(DEFAULT_EOIU_HOLD_INTERVAL + 5 +1) + swss_app_check_warmstart_state(state_db, "bgp", "restored") + # Fall back to warm restart timer, it kicks in after 15 seconds, +1 to avoid race condition: + time.sleep(restart_timer - DEFAULT_EOIU_HOLD_INTERVAL -5 ) + swss_app_check_warmstart_state(state_db, "bgp", "reconciled") + + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAppDbObjects(pubsubAppDB) + assert len(addobjs) == 0 and len(delobjs) == 0 + + # Verify swss changes -- none are expected this time + (addobjs, delobjs) = dvs.GetSubscribedAsicDbObjects(pubsubAsicDB) + assert len(addobjs) == 0 and len(delobjs) == 0 + + intf_tbl._del("{}|111.0.0.1/24".format(intfs[0])) + intf_tbl._del("{}|1110::1/64".format(intfs[0])) + intf_tbl._del("{}|122.0.0.1/24".format(intfs[1])) + intf_tbl._del("{}|1220::1/64".format(intfs[1])) + intf_tbl._del("{}|133.0.0.1/24".format(intfs[2])) + intf_tbl._del("{}|1330::1/64".format(intfs[2])) + intf_tbl._del("{}".format(intfs[0])) + intf_tbl._del("{}".format(intfs[0])) + intf_tbl._del("{}".format(intfs[1])) + intf_tbl._del("{}".format(intfs[1])) + intf_tbl._del("{}".format(intfs[2])) + intf_tbl._del("{}".format(intfs[2])) + time.sleep(2) + + def test_system_warmreboot_neighbor_syncup(self, dvs, testlog): + + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + #enable ipv6 on docker + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + + # flush all neighs first + flush_neigh_entries(dvs) + time.sleep(5) + + dvs.runcmd("config warm_restart enable system") + + # Test neighbors on NUM_INTF (e,g 8) interfaces + # Ethernet32/36/.../60, with ip: 32.0.0.1/24... 60.0.0.1/24 + # ipv6: 3200::1/64...6000::1/64 + # bring up the servers'interfaces and assign NUM_NEIGH_PER_INTF (e,g 128) ips per interface + macs = [] + intf_tbl = swsscommon.Table(conf_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + for i in range(8, 8+NUM_INTF): + # set timeout to be the same as real HW + # set stale timer bigger to avoid testbed difference related timing issues. + # set ip on server facing interfaces + # bring servers' interface up, save the macs + dvs.runcmd("sysctl -w net.ipv4.neigh.Ethernet{}.base_reachable_time_ms=1800000".format(i*4)) + dvs.runcmd("sysctl -w net.ipv6.neigh.Ethernet{}.base_reachable_time_ms=1800000".format(i*4)) + dvs.runcmd("sysctl -w net.ipv4.neigh.Ethernet{}.gc_stale_time=600".format(i*4)) + dvs.runcmd("sysctl -w net.ipv6.neigh.Ethernet{}.gc_stale_time=600".format(i*4)) + dvs.runcmd("ip addr flush dev Ethernet{}".format(i*4)) + intf_tbl.set("Ethernet{}|{}.0.0.1/24".format(i*4, i*4), fvs) + intf_tbl.set("Ethernet{}|{}00::1/64".format(i*4, i*4), fvs) + intf_tbl.set("Ethernet{}".format(i*4, i*4), fvs) + intf_tbl.set("Ethernet{}".format(i*4, i*4), fvs) + dvs.runcmd("ip link set Ethernet{} up".format(i*4, i*4)) + dvs.servers[i].runcmd("ip link set up dev eth0") + dvs.servers[i].runcmd("ip addr flush dev eth0") + #result = dvs.servers[i].runcmd_output("ifconfig eth0 | grep HWaddr | awk '{print $NF}'") + result = dvs.servers[i].runcmd_output("cat /sys/class/net/eth0/address") + macs.append(result.strip()) + + # + # Testcase 1: + # Setup initial neigbors + setup_initial_neighbors(dvs) + + # Check the neighbor entries are inserted correctly + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, "NEIGH_TABLE") + + # number of neighbors should match what we configured + # ipv4/ipv6 entries and loopback + check_redis_neigh_entries(dvs, tbl, 2*NUM_OF_NEIGHS) + + # All neighbor entries should match + for i in range(8, 8+NUM_INTF): + for j in range(NUM_NEIGH_PER_INTF): + (status, fvs) = tbl.get("Ethernet{}:{}.0.0.{}".format(i*4, i*4, j+2)) + assert status == True + for v in fvs: + if v[0] == "family": + assert v[1] == "IPv4" + if v[0] == "neigh": + assert v[1] == macs[i-8] + + (status, fvs) = tbl.get("Ethernet{}:{}00::{}".format(i*4, i*4, j+2)) + assert status == True + for v in fvs: + if v[0] == "family": + assert v[1] == "IPv6" + if v[0] == "neigh": + assert v[1] == macs[i-8] + + # + # Testcase 2: + # Stop neighsyncd, appDB entries should be reserved + # flush kernel neigh table to simulate warm reboot + # start neighsyncd, start restore_neighbors service to restore the neighbor table in kernel + # check all neighbors learned in kernel + # no changes should be there in syslog and sairedis.rec + + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) + + # stop neighsyncd and sairedis.rec + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + time.sleep(3) + flush_neigh_entries(dvs) + time.sleep(3) + + # check neighbors are gone + check_kernel_reachable_neigh_num(dvs, 0) + + # start neighsyncd and restore_neighbors + marker = dvs.add_log_marker() + pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + start_neighsyncd(dvs) + start_restore_neighbors(dvs) + + # should finish the store within 10 seconds + time.sleep(10) - # Check the neighbor entries are inserted correctly - db = swsscommon.DBConnector(0, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, "NEIGH_TABLE") + check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS) + check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS) - # number of neighbors should match what we configured - # ipv4/ipv6 entries and loopback - check_redis_neigh_entries(dvs, tbl, 2*NUM_OF_NEIGHS) + # check syslog and sairedis.rec file for activities + check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == 0 - # All neighbor entries should match - for i in range(8, 8+NUM_INTF): - for j in range(NUM_NEIGH_PER_INTF): - (status, fvs) = tbl.get("Ethernet{}:{}.0.0.{}".format(i*4, i*4, j+2)) - assert status == True - for v in fvs: - if v[0] == "family": - assert v[1] == "IPv4" - if v[0] == "neigh": - assert v[1] == macs[i-8] + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - (status, fvs) = tbl.get("Ethernet{}:{}00::{}".format(i*4, i*4, j+2)) - assert status == True - for v in fvs: - if v[0] == "family": - assert v[1] == "IPv6" - if v[0] == "neigh": - assert v[1] == macs[i-8] + # + # Testcase 3: + # Stop neighsyncd, appDB entries should be reserved + # flush kernel neigh table to simulate warm reboot + # Remove half of ips of servers' interfaces, add new half of ips + # start neighsyncd, start restore_neighbors service to restore the neighbor table in kernel + # check all new neighbors learned in kernel + # no changes should be there in syslog and sairedis.rec - # - # Testcase 2: - # Stop neighsyncd, appDB entries should be reserved - # flush kernel neigh table to simulate warm reboot - # start neighsyncd, start restore_neighbors service to restore the neighbor table in kernel - # check all neighbors learned in kernel - # no changes should be there in syslog and sairedis.rec - - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) - - # stop neighsyncd and sairedis.rec - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - time.sleep(3) - flush_neigh_entries(dvs) - time.sleep(3) - - # check neighbors are gone - check_kernel_reachable_neigh_num(dvs, 0) - - # start neighsyncd and restore_neighbors - marker = dvs.add_log_marker() - pubsub = dvs.SubscribeAsicDbObject("SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") - start_neighsyncd(dvs) - start_restore_neighbors(dvs) - - # should finish the store within 10 seconds - time.sleep(10) - - check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS) - check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS) - - # check syslog and sairedis.rec file for activities - check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == 0 - - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - - # - # Testcase 3: - # Stop neighsyncd, appDB entries should be reserved - # flush kernel neigh table to simulate warm reboot - # Remove half of ips of servers' interfaces, add new half of ips - # start neighsyncd, start restore_neighbors service to restore the neighbor table in kernel - # check all new neighbors learned in kernel - # no changes should be there in syslog and sairedis.rec + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) + # stop neighsyncd and sairedis.rec + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + time.sleep(3) - # stop neighsyncd and sairedis.rec - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - time.sleep(3) + del_and_add_neighbors(dvs) - del_and_add_neighbors(dvs) + flush_neigh_entries(dvs) + time.sleep(3) - flush_neigh_entries(dvs) - time.sleep(3) + # check neighbors are gone + check_kernel_reachable_neigh_num(dvs, 0) - # check neighbors are gone - check_kernel_reachable_neigh_num(dvs, 0) + # start neighsyncd and restore_neighbors + marker = dvs.add_log_marker() + start_neighsyncd(dvs) + start_restore_neighbors(dvs) - # start neighsyncd and restore_neighbors - marker = dvs.add_log_marker() - start_neighsyncd(dvs) - start_restore_neighbors(dvs) + # should finish the store within 10 seconds + time.sleep(10) - # should finish the store within 10 seconds - time.sleep(10) + check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) + check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) + check_kernel_stale_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) + check_kernel_stale_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_stale_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_stale_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) + # check syslog and sairedis.rec file for activities + check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == 0 - # check syslog and sairedis.rec file for activities - check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 0, 0, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == 0 + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") + # Test case 4: + # ping the new ips, should get it into appDB + marker = dvs.add_log_marker() - # Test case 4: - # ping the new ips, should get it into appDB - marker = dvs.add_log_marker() + ping_new_ips(dvs) - ping_new_ips(dvs) + check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS) + check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS) - check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS) - check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS) + check_kernel_stale_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) + check_kernel_stale_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_stale_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_stale_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) + check_redis_neigh_entries(dvs, tbl, 2*(NUM_OF_NEIGHS+NUM_OF_NEIGHS/2)) - check_redis_neigh_entries(dvs, tbl, 2*(NUM_OF_NEIGHS+NUM_OF_NEIGHS/2)) + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == NUM_OF_NEIGHS #ipv4 and ipv6 + assert ndel == 0 - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == NUM_OF_NEIGHS #ipv4 and ipv6 - assert ndel == 0 + # Remove stale entries manually + for i in range(8, 8+NUM_INTF): + for j in range(NUM_NEIGH_PER_INTF/2): + dvs.runcmd(['sh', '-c', "ip neigh del {}.0.0.{} dev Ethernet{}".format(i*4,j+NUM_NEIGH_PER_INTF/2+2, i*4)]) + dvs.runcmd(['sh', '-c', "ip -6 neigh del {}00::{} dev Ethernet{}".format(i*4,j+NUM_NEIGH_PER_INTF/2+2, i*4)]) - # Remove stale entries manually - for i in range(8, 8+NUM_INTF): - for j in range(NUM_NEIGH_PER_INTF/2): - dvs.runcmd(['sh', '-c', "ip neigh del {}.0.0.{} dev Ethernet{}".format(i*4,j+NUM_NEIGH_PER_INTF/2+2, i*4)]) - dvs.runcmd(['sh', '-c', "ip -6 neigh del {}00::{} dev Ethernet{}".format(i*4,j+NUM_NEIGH_PER_INTF/2+2, i*4)]) + time.sleep(5) - time.sleep(5) + check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS) + check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS) - check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS) - check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS) + check_kernel_stale_v4_neigh_num(dvs, 0) + check_kernel_stale_v6_neigh_num(dvs, 0) - check_kernel_stale_v4_neigh_num(dvs, 0) - check_kernel_stale_v6_neigh_num(dvs, 0) + check_redis_neigh_entries(dvs, tbl, 2*NUM_OF_NEIGHS) - check_redis_neigh_entries(dvs, tbl, 2*NUM_OF_NEIGHS) + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == NUM_OF_NEIGHS #ipv4 and ipv6 - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == NUM_OF_NEIGHS #ipv4 and ipv6 + # + # Testcase 5: + # Stop neighsyncd, appDB entries should be reserved + # flush kernel neigh table to simulate warm reboot + # keep half of the interface down + # start neighsyncd, start restore_neighbors service to restore the neighbor table in kernel + # check all new neighbors with interface up to be learned in kernel + # syslog/sai log should show half of the entries stale/deleted - # - # Testcase 5: - # Stop neighsyncd, appDB entries should be reserved - # flush kernel neigh table to simulate warm reboot - # keep half of the interface down - # start neighsyncd, start restore_neighbors service to restore the neighbor table in kernel - # check all new neighbors with interface up to be learned in kernel - # syslog/sai log should show half of the entries stale/deleted + # get restore_count + restore_count = swss_get_RestoreCount(dvs, state_db) - # get restore_count - restore_count = swss_get_RestoreCount(dvs, state_db) + # stop neighsyncd and sairedis.rec + stop_neighsyncd(dvs) + del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") + time.sleep(3) - # stop neighsyncd and sairedis.rec - stop_neighsyncd(dvs) - del_entry_tbl(state_db, "NEIGH_RESTORE_TABLE", "Flags") - time.sleep(3) + flush_neigh_entries(dvs) + time.sleep(3) - flush_neigh_entries(dvs) - time.sleep(3) + # check neighbors are gone + check_kernel_reachable_neigh_num(dvs, 0) - # check neighbors are gone - check_kernel_reachable_neigh_num(dvs, 0) + # bring down half of the links + for i in range(8, 8+NUM_INTF/2): + dvs.runcmd("ip link set down dev Ethernet{}".format(i*4)) - # bring down half of the links - for i in range(8, 8+NUM_INTF/2): - dvs.runcmd("ip link set down dev Ethernet{}".format(i*4)) + # start neighsyncd and restore_neighbors + marker = dvs.add_log_marker() + start_neighsyncd(dvs) + start_restore_neighbors(dvs) - # start neighsyncd and restore_neighbors - marker = dvs.add_log_marker() - start_neighsyncd(dvs) - start_restore_neighbors(dvs) + # restore for up interfaces should be done within 10 seconds + time.sleep(10) - # restore for up interfaces should be done within 10 seconds - time.sleep(10) + check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) + check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_reachable_v4_neigh_num(dvs, NUM_OF_NEIGHS/2) - check_kernel_reachable_v6_neigh_num(dvs, NUM_OF_NEIGHS/2) + restoretbl = swsscommon.Table(state_db, swsscommon.STATE_NEIGH_RESTORE_TABLE_NAME) - restoretbl = swsscommon.Table(state_db, swsscommon.STATE_NEIGH_RESTORE_TABLE_NAME) + # waited 10 above already + i = 10 + while (not kernel_restore_neighs_done(restoretbl)): + print "Waiting for kernel neighbors restore process done: {} seconds".format(i) + time.sleep(10) + i += 10 - # waited 10 above already - i = 10 - while (not kernel_restore_neighs_done(restoretbl)): - print "Waiting for kernel neighbors restore process done: {} seconds".format(i) time.sleep(10) - i += 10 - - time.sleep(10) - # check syslog and sairedis.rec file for activities - check_syslog_for_neighbor_entry(dvs, marker, 0, NUM_OF_NEIGHS/2, "ipv4") - check_syslog_for_neighbor_entry(dvs, marker, 0, NUM_OF_NEIGHS/2, "ipv6") - (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) - assert nadd == 0 - assert ndel == NUM_OF_NEIGHS - # check restore Count - swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") + # check syslog and sairedis.rec file for activities + check_syslog_for_neighbor_entry(dvs, marker, 0, NUM_OF_NEIGHS/2, "ipv4") + check_syslog_for_neighbor_entry(dvs, marker, 0, NUM_OF_NEIGHS/2, "ipv6") + (nadd, ndel) = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == NUM_OF_NEIGHS - # disable system warm restart - dvs.runcmd("config warm_restart disable system") + # check restore Count + swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") - for i in range(8, 8+NUM_INTF): - intf_tbl._del("Ethernet{}|{}.0.0.1/24".format(i*4, i*4)) - intf_tbl._del("Ethernet{}|{}00::1/64".format(i*4, i*4)) - intf_tbl._del("Ethernet{}".format(i*4, i*4)) - intf_tbl._del("Ethernet{}".format(i*4, i*4)) + # disable system warm restart + dvs.runcmd("config warm_restart disable system") + for i in range(8, 8+NUM_INTF): + intf_tbl._del("Ethernet{}|{}.0.0.1/24".format(i*4, i*4)) + intf_tbl._del("Ethernet{}|{}00::1/64".format(i*4, i*4)) + intf_tbl._del("Ethernet{}".format(i*4, i*4)) + intf_tbl._del("Ethernet{}".format(i*4, i*4)) diff --git a/warmrestart/warmRestartAssist.h b/warmrestart/warmRestartAssist.h index 2d5e387ee13..227815c264e 100644 --- a/warmrestart/warmRestartAssist.h +++ b/warmrestart/warmRestartAssist.h @@ -108,7 +108,7 @@ class AppRestartAssist std::string m_appTableName; // application table name bool m_warmStartInProgress; // indicate if warm start is in progress - uint32_t m_reconcileTimer; // reconcile timer value + time_t m_reconcileTimer; // reconcile timer value SelectableTimer m_warmStartTimer; // reconcile timer // Set or get cache entry state