diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 147c87459c6e..d4b40844a8fb 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -45,6 +45,7 @@ QosOrch *gQosOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; +PolicerOrch *gPolicerOrch; MlagOrch *gMlagOrch; IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; @@ -242,11 +243,17 @@ bool OrchDaemon::init() }; gBufferOrch = new BufferOrch(m_applDb, m_configDb, m_stateDb, buffer_tables); - PolicerOrch *policer_orch = new PolicerOrch(m_configDb, "POLICER"); + vector policer_tables = { + TableConnector(m_configDb, CFG_POLICER_TABLE_NAME), + TableConnector(m_configDb, CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + + TableConnector stateDbStorm(m_stateDb, "BUM_STORM_CAPABILITY"); + gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch); TableConnector stateDbMirrorSession(m_stateDb, STATE_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_configDb, CFG_MIRROR_SESSION_TABLE_NAME); - gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, policer_orch); + gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch); TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_TABLE_NAME); TableConnector confDbAclTableType(m_configDb, CFG_ACL_TABLE_TYPE_TABLE_NAME); @@ -339,7 +346,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, gDebugCounterOrch, gMacsecOrch, gBfdOrch, gSrv6Orch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, tunnel_decap_orch, sflow_orch, gDebugCounterOrch, gMacsecOrch, gBfdOrch, gSrv6Orch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/policerorch.cpp b/orchagent/policerorch.cpp index c4528f633041..68dfffe898ee 100644 --- a/orchagent/policerorch.cpp +++ b/orchagent/policerorch.cpp @@ -8,10 +8,13 @@ using namespace std; using namespace swss; extern sai_policer_api_t* sai_policer_api; +extern sai_port_api_t *sai_port_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +#define ETHERNET_PREFIX "Ethernet" + static const string meter_type_field = "METER_TYPE"; static const string mode_field = "MODE"; static const string color_source_field = "COLOR_SOURCE"; @@ -23,6 +26,11 @@ static const string green_packet_action_field = "GREEN_PACKET_ACTION"; static const string red_packet_action_field = "RED_PACKET_ACTION"; static const string yellow_packet_action_field = "YELLOW_PACKET_ACTION"; +static const string storm_control_kbps = "KBPS"; +static const string storm_broadcast = "broadcast"; +static const string storm_unknown_unicast = "unknown-unicast"; +static const string storm_unknown_mcast = "unknown-multicast"; + static const map meter_type_map = { {"PACKETS", SAI_METER_TYPE_PACKETS}, {"BYTES", SAI_METER_TYPE_BYTES} @@ -105,15 +113,268 @@ bool PolicerOrch::decreaseRefCount(const string &name) return true; } -PolicerOrch::PolicerOrch(DBConnector* db, string tableName) : - Orch(db, tableName) +PolicerOrch::PolicerOrch(vector &tableNames, PortsOrch *portOrch) : Orch(tableNames), m_portsOrch(portOrch) { SWSS_LOG_ENTER(); } +task_process_status PolicerOrch::handlePortStormControlTable(swss::KeyOpFieldsValuesTuple tuple) +{ + auto key = kfvKey(tuple); + auto op = kfvOp(tuple); + string storm_key = key; + auto tokens = tokenize(storm_key, config_db_key_delimiter); + auto interface_name = tokens[0]; + auto storm_type = tokens[1]; + Port port; + + /*Only proceed for Ethernet interfaces*/ + if (strncmp(interface_name.c_str(), ETHERNET_PREFIX, strlen(ETHERNET_PREFIX))) + { + SWSS_LOG_ERROR("%s: Unsupported / Invalid interface %s", + storm_type.c_str(), interface_name.c_str()); + return task_process_status::task_success; + } + if (!gPortsOrch->getPort(interface_name, port)) + { + SWSS_LOG_ERROR("Failed to apply storm-control %s to port %s. Port not found", + storm_type.c_str(), interface_name.c_str()); + /*continue here as there can be more interfaces*/ + return task_process_status::task_success; + } + /*Policer Name: __*/ + const auto storm_policer_name = "_"+interface_name+"_"+storm_type; + + if (op == SET_COMMAND) + { + // Mark the operation as an 'update', if the policer exists. + bool update = m_syncdPolicers.find(storm_policer_name) != m_syncdPolicers.end(); + vector attrs; + bool cir = false; + sai_attribute_t attr; + + /*Meter type hardcoded to BYTES*/ + attr.id = SAI_POLICER_ATTR_METER_TYPE; + attr.value.s32 = (sai_meter_type_t) meter_type_map.at("BYTES"); + attrs.push_back(attr); + + /*Policer mode hardcoded to STORM_CONTROL*/ + attr.id = SAI_POLICER_ATTR_MODE; + attr.value.s32 = (sai_policer_mode_t) policer_mode_map.at("STORM_CONTROL"); + attrs.push_back(attr); + + /*Red Packet Action hardcoded to DROP*/ + attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION; + attr.value.s32 = packet_action_map.at("DROP"); + attrs.push_back(attr); + + for (auto i = kfvFieldsValues(tuple).begin(); + i != kfvFieldsValues(tuple).end(); ++i) + { + auto field = to_upper(fvField(*i)); + auto value = to_upper(fvValue(*i)); + + /*BPS value is used as CIR*/ + if (field == storm_control_kbps) + { + attr.id = SAI_POLICER_ATTR_CIR; + /*convert kbps to bps*/ + attr.value.u64 = (stoul(value)*1000/8); + cir = true; + attrs.push_back(attr); + SWSS_LOG_DEBUG("CIR %s",value.c_str()); + } + else + { + SWSS_LOG_ERROR("Unknown storm control attribute %s specified", + field.c_str()); + continue; + } + } + /*CIR is mandatory parameter*/ + if (!cir) + { + SWSS_LOG_ERROR("Failed to create storm control policer %s,\ + missing mandatory fields", storm_policer_name.c_str()); + return task_process_status::task_failed; + } + + /*Enabling storm-control on port*/ + sai_attribute_t port_attr; + if (storm_type == storm_broadcast) + { + port_attr.id = SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_unicast) + { + port_attr.id = SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_mcast) + { + port_attr.id = SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID; + } + else + { + SWSS_LOG_ERROR("Unknown storm_type %s", storm_type.c_str()); + return task_process_status::task_failed; + } + + sai_object_id_t policer_id; + // Create a new policer + if (!update) + { + 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", + storm_policer_name.c_str(), status); + if (handleSaiCreateStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + + SWSS_LOG_DEBUG("Created storm-control policer %s", storm_policer_name.c_str()); + m_syncdPolicers[storm_policer_name] = policer_id; + m_policerRefCounts[storm_policer_name] = 0; + } + // Update an existing policer + else + { + policer_id = m_syncdPolicers[storm_policer_name]; + + // The update operation has limitations that it could only update + // the rate and the size accordingly. + // STORM_CONTROL: CIR, CBS + for (auto & attr: attrs) + { + if (attr.id != SAI_POLICER_ATTR_CIR) + { + 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", + storm_policer_name.c_str(), status); + if (handleSaiSetStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + + } + } + } + policer_id = m_syncdPolicers[storm_policer_name]; + + if (update) + { + SWSS_LOG_NOTICE("update storm-control policer %s", storm_policer_name.c_str()); + port_attr.value.oid = SAI_NULL_OBJECT_ID; + /*Remove and re-apply policer*/ + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove storm-control %s from port %s, rv:%d", + storm_type.c_str(), interface_name.c_str(), status); + if (handleSaiSetStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + } + port_attr.value.oid = policer_id; + + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to apply storm-control %s to port %s, rv:%d", + storm_type.c_str(), interface_name.c_str(),status); + + /*TODO: Do the below policer cleanup in an API*/ + /*Remove the already created policer*/ + if (SAI_STATUS_SUCCESS != sai_policer_api->remove_policer( + m_syncdPolicers[storm_policer_name])) + { + SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d", + storm_policer_name.c_str(), status); + /*TODO: Just doing a syslog. */ + } + + SWSS_LOG_NOTICE("Removed policer %s as set_port_attribute for %s failed", + storm_policer_name.c_str(),interface_name.c_str()); + m_syncdPolicers.erase(storm_policer_name); + m_policerRefCounts.erase(storm_policer_name); + + return task_process_status::task_need_retry; + } + } + else if (op == DEL_COMMAND) + { + if (m_syncdPolicers.find(storm_policer_name) == m_syncdPolicers.end()) + { + SWSS_LOG_ERROR("Policer %s not configured", storm_policer_name.c_str()); + return task_process_status::task_success; + } + + sai_attribute_t port_attr; + if (storm_type == storm_broadcast) + { + port_attr.id = SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_unicast) + { + port_attr.id = SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_mcast) + { + port_attr.id = SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID; + } + else + { + SWSS_LOG_ERROR("Unknown storm_type %s", storm_type.c_str()); + return task_process_status::task_failed; + } + + port_attr.value.oid = SAI_NULL_OBJECT_ID; + + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove storm-control %s from port %s, rv:%d", + storm_type.c_str(), interface_name.c_str(), status); + if (handleSaiRemoveStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + + status = sai_policer_api->remove_policer( + m_syncdPolicers[storm_policer_name]); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d", + storm_policer_name.c_str(), status); + if (handleSaiRemoveStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + + SWSS_LOG_NOTICE("Removed policer %s", storm_policer_name.c_str()); + m_syncdPolicers.erase(storm_policer_name); + m_policerRefCounts.erase(storm_policer_name); + } + return task_process_status::task_success; +} + void PolicerOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); + task_process_status storm_status = task_success; if (!gPortsOrch->allPortsReady()) { @@ -127,7 +388,23 @@ void PolicerOrch::doTask(Consumer &consumer) auto key = kfvKey(tuple); auto op = kfvOp(tuple); + auto table_name = consumer.getTableName(); + // Special handling for storm-control configuration. + if (table_name == CFG_PORT_STORM_CONTROL_TABLE_NAME) + { + storm_status = handlePortStormControlTable(tuple); + if ((storm_status == task_process_status::task_success) || + (storm_status == task_process_status::task_failed)) + { + it = consumer.m_toSync.erase(it); + } + else + { + it++; + } + continue; + } if (op == SET_COMMAND) { // Mark the operation as an 'update', if the policer exists. diff --git a/orchagent/policerorch.h b/orchagent/policerorch.h index d735da03b793..9814179958de 100644 --- a/orchagent/policerorch.h +++ b/orchagent/policerorch.h @@ -14,16 +14,20 @@ typedef map PolicerRefCountTable; class PolicerOrch : public Orch { public: - PolicerOrch(DBConnector* db, string tableName); + PolicerOrch(vector &tableNames, PortsOrch *portOrch); bool policerExists(const string &name); bool getPolicerOid(const string &name, sai_object_id_t &oid); bool increaseRefCount(const string &name); bool decreaseRefCount(const string &name); + task_process_status handlePortStormControlTable(swss::KeyOpFieldsValuesTuple tuple); private: + PortsOrch *m_portsOrch; virtual void doTask(Consumer& consumer); PolicerTable m_syncdPolicers; PolicerRefCountTable m_policerRefCounts; }; + + diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 6f58b12a3b35..0d81c93f69a7 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -426,7 +426,12 @@ namespace aclorch_test }; gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); - PolicerOrch *policer_orch = new PolicerOrch(m_config_db.get(), "POLICER"); + vector policer_tables = { + TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), + TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + TableConnector stateDbStorm(m_state_db.get(), "BUM_STORM_CAPABILITY"); + PolicerOrch *policer_orch = new PolicerOrch(policer_tables, gPortsOrch); TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME); diff --git a/tests/mock_tests/copporch_ut.cpp b/tests/mock_tests/copporch_ut.cpp index 36ba71bc6774..fa7c360f01ba 100644 --- a/tests/mock_tests/copporch_ut.cpp +++ b/tests/mock_tests/copporch_ut.cpp @@ -203,7 +203,11 @@ namespace copporch_test // PolicerOrch // - auto policerOrch = new PolicerOrch(this->configDb.get(), CFG_POLICER_TABLE_NAME); + vector policer_tables = { + TableConnector(this->configDb.get(), CFG_POLICER_TABLE_NAME), + TableConnector(this->configDb.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + auto policerOrch = new PolicerOrch(policer_tables, gPortsOrch); gDirectory.set(policerOrch); resourcesList.push_back(policerOrch); diff --git a/tests/test_storm_control.py b/tests/test_storm_control.py new file mode 100644 index 000000000000..76deef926878 --- /dev/null +++ b/tests/test_storm_control.py @@ -0,0 +1,316 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion +import pytest + +class TestStormControl(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) + self.sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + dvs.runcmd(['sh', '-c', "echo 0 > /var/log/syslog"]) + + def create_port_channel(self, dvs, lag_name): + dvs.runcmd("config portchannel add " + lag_name) + time.sleep(1) + + def delete_port_channel(self, dvs, lag_name): + dvs.runcmd("config portchannel del " + lag_name) + time.sleep(1) + + def add_port_channel_member(self, dvs, lag_name, member): + dvs.runcmd("config portchannel member add "+ lag_name + " "+ member) + time.sleep(1) + + def remove_port_channel_member(self, dvs, lag_name, member): + dvs.runcmd("config portchannel member del "+ lag_name + " "+ member) + time.sleep(1) + + def create_vlan(self, dvs, vlan): + dvs.runcmd("config vlan add " + vlan) + time.sleep(1) + + def delete_vlan(self, dvs, vlan): + dvs.runcmd("config vlan del " + vlan) + time.sleep(1) + + def add_vlan_member(self, dvs, vlan, interface): + dvs.runcmd("config vlan member add " + vlan + " " + interface) + time.sleep(1) + + def remove_vlan_member(self, dvs, vlan, interface): + dvs.runcmd("config vlan member del " + vlan + " " + interface) + time.sleep(1) + + def add_storm_session(self, if_name, storm_type, kbps_value): + tbl = swsscommon.Table(self.cdb, "PORT_STORM_CONTROL") + fvs = swsscommon.FieldValuePairs([("kbps", str(kbps_value))]) + key = if_name + "|" + storm_type + tbl.set(key,fvs) + time.sleep(1) + + def delete_storm_session(self, if_name, storm_type): + tbl = swsscommon.Table(self.cdb, "PORT_STORM_CONTROL") + key = if_name + "|" + storm_type + tbl._del(key) + time.sleep(1) + + def test_bcast_storm(self,dvs,testlog): + self.setup_db(dvs) + + if_name = "Ethernet0" + storm_type = "broadcast" + #User input is Kbps + #Orchagent converts the value to CIR as below and programs the ASIC DB + #kbps_value * 1000 / 8 + kbps_value = 1000000 + self.add_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + self.del_storm_control(dvs,if_name,storm_type) + + def del_storm_control(self, dvs, if_name, storm_type): + self.setup_db(dvs) + port_oid = dvs.asicdb.portnamemap[if_name] + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + status, fvs = atbl.get(dvs.asicdb.portnamemap[if_name]) + assert status == True + + storm_type_port_attr = self.get_port_attr_for_storm_type(storm_type) + + policer_oid = 0 + for fv in fvs: + if fv[0] == storm_type_port_attr: + policer_oid = fv[1] + + self.delete_storm_session(if_name, storm_type) + tbl = swsscommon.Table(self.cdb, "PORT_STORM_CONTROL") + (status,fvs) = tbl.get(if_name+"|"+storm_type) + assert status == False + + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_PORT") + status, fvs = atbl.get(dvs.asicdb.portnamemap[if_name]) + assert status == True + + for fv in fvs: + if fv[0] == storm_type_port_attr: + assert fv[1] == "oid:0x0" + + if policer_oid != 0: + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + status, fvs = atbl.get(policer_oid) + assert status == False + + def test_uucast_storm(self,dvs,testlog): + self.setup_db(dvs) + + if_name = "Ethernet0" + storm_type = "unknown-unicast" + #User input is Kbps + #Orchagent converts the value to CIR as below and programs the ASIC DB + #kbps_value * 1000 / 8 + kbps_value = 1000000 + + self.add_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + self.del_storm_control(dvs,if_name,storm_type) + + def test_umcast_storm(self,dvs,testlog): + self.setup_db(dvs) + + if_name = "Ethernet0" + storm_type = "unknown-multicast" + #User input is Kbps + #Orchagent converts the value to CIR as below and programs the ASIC DB + #kbps_value * 1000 / 8 + kbps_value = 1000000 + + self.add_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + self.del_storm_control(dvs,if_name,storm_type) + + def get_port_attr_for_storm_type(self,storm_type): + port_attr = "" + if storm_type == "broadcast": + port_attr = "SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID" + elif storm_type == "unknown-unicast": + port_attr = "SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID" + elif storm_type == "unknown-multicast": + port_attr = "SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID" + + return port_attr + + def check_storm_control_on_interface(self,dvs,if_name,storm_type,kbps_value): + print ("interface {} storm_type {} kbps {}".format(if_name,storm_type, kbps_value)) + tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + (status,fvs) = tbl.get(if_name+"|"+storm_type) + + assert status == True + assert len(fvs) > 0 + + port_oid = dvs.asicdb.portnamemap[if_name] + + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_PORT") + status, fvs = atbl.get(dvs.asicdb.portnamemap[if_name]) + assert status == True + + policer_oid = 0 + + storm_type_port_attr = self.get_port_attr_for_storm_type(storm_type) + + for fv in fvs: + if fv[0] == storm_type_port_attr: + assert fv[1] != "oid:0x0" + policer_oid = fv[1] + + if policer_oid != 0: + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + status, fvs = atbl.get(policer_oid) + assert status == True + + bps = 0 + + for fv in fvs: + if fv[0] == "SAI_POLICER_ATTR_CIR": + bps = fv[1] + + #Retrieved value of bps from ASIC_DB is converted back to user input kbps + kbps = int(int(bps) / int(1000) * 8) + print ("Kbps value {}".format(kbps)) + + assert str(kbps) == str(kbps_value) + + + def add_storm_control_on_interface(self,dvs,if_name,storm_type,kbps_value): + print ("interface {} storm_type {} kbps {}".format(if_name,storm_type,kbps_value)) + self.add_storm_session(if_name, storm_type, kbps_value) + self.check_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + + def test_add_storm_all_interfaces(self,dvs,testlog): + self.setup_db(dvs) + + tbl = swsscommon.Table(self.cdb,"PORT") + for key in tbl.getKeys(): + self.add_storm_control_on_interface(dvs,key,"broadcast",1000000) + self.add_storm_control_on_interface(dvs,key,"unknown-unicast",2000000) + self.add_storm_control_on_interface(dvs,key,"unknown-multicast",3000000) + self.del_storm_control(dvs,key,"broadcast") + self.del_storm_control(dvs,key,"unknown-unicast") + self.del_storm_control(dvs,key,"unknown-multicast") + + def test_warm_restart_all_interfaces(self,dvs,testlog): + self.setup_db(dvs) + + tbl = swsscommon.Table(self.cdb,"PORT") + for key in tbl.getKeys(): + self.add_storm_control_on_interface(dvs,key,"broadcast",1000000) + self.add_storm_control_on_interface(dvs,key,"unknown-unicast",2000000) + self.add_storm_control_on_interface(dvs,key,"unknown-multicast",3000000) + #dvs.runcmd("config save -y") + # enable warm restart + dvs.warm_restart_swss("true") + + # freeze orchagent for warm restart + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK succeeded\n" + time.sleep(2) + + dvs.stop_swss() + time.sleep(10) + dvs.start_swss() + time.sleep(10) + + for key in tbl.getKeys(): + self.check_storm_control_on_interface(dvs,key,"broadcast",1000000) + self.check_storm_control_on_interface(dvs,key,"unknown-unicast",2000000) + self.check_storm_control_on_interface(dvs,key,"unknown-multicast",3000000) + self.del_storm_control(dvs,key,"broadcast") + self.del_storm_control(dvs,key,"unknown-unicast") + self.del_storm_control(dvs,key,"unknown-multicast") + # disable warm restart + dvs.warm_restart_swss("false") + + def test_add_storm_lag_interface(self,dvs,testlog): + self.setup_db(dvs) + lag_name = "PortChannel10" + member_interface = "Ethernet0" + kbps_value = 1000000 + storm_list = ["broadcast","unknown-unicast","unknown-multicast"] + kbps_value_list = [1000000,2000000,3000000] + + #Create LAG interface and add member + self.create_port_channel(dvs,lag_name) + self.add_port_channel_member(dvs,lag_name,member_interface) + + #click CLI verification + #for storm_type in storm_list: + # dvs.runcmd("config interface storm-control add "+lag_name+" "+storm_type+" "+str(kbps_value)) + # tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + # (status,fvs) = tbl.get(lag_name+"|"+storm_type) + # assert status == False + # assert len(fvs) == 0 + + #Orchagent verification + storm_list_db = ["broadcast","unknown-unicast","unknown-multicast"] + for storm_type,kbps_value in zip(storm_list_db,kbps_value_list): + #Cleanup syslog + dvs.runcmd(['sh', '-c', "echo 0 > /var/log/syslog"]) + time.sleep(1) + print ("storm type: {} kbps value: {}".format(storm_type,kbps_value)) + #Add storm entry to config DB directly + self.add_storm_session(lag_name,storm_type,kbps_value) + tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + (status,fvs) = tbl.get(lag_name+"|"+storm_type) + assert status == True + assert len(fvs) > 0 + time.sleep(1) + #grep for error message in syslog + (exitcode,num) = dvs.runcmd(['sh', '-c', 'cat /var/log/syslog | grep -i "handlePortStormControlTable: {}: Unsupported / Invalid interface PortChannel10"'.format(storm_type)]) + time.sleep(1) + assert exitcode == 0 + self.delete_storm_session(lag_name, storm_type) + self.remove_port_channel_member(dvs,lag_name,member_interface) + self.delete_port_channel(dvs,lag_name) + + def test_add_storm_vlan_interface(self,dvs,testlog): + self.setup_db(dvs) + vlan_id = 99 + member_interface = "Ethernet4" + kbps_value = 1000000 + storm_list = ["broadcast","unknown-unicast","unknown-multicast"] + kbps_value_list = [1000000,2000000,3000000] + vlan_name = "Vlan"+str(vlan_id) + + #Create VLAN interface and add member + self.create_vlan(dvs,str(vlan_id)) + self.add_vlan_member(dvs,str(vlan_id),member_interface) + + #click CLI verification + #for storm_type in storm_list: + # dvs.runcmd("config interface storm-control add Vlan"+str(vlan_id)+" "+storm_type+" "+str(kbps_value)) + # tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + # (status,fvs) = tbl.get("Vlan"+str(vlan_id)+"|"+storm_type) + # assert status == False + # assert len(fvs) == 0 + + #Orchagent verification + storm_list_db = ["broadcast","unknown-unicast","unknown-multicast"] + for storm_type,kbps_value in zip(storm_list_db,kbps_value_list): + #Cleanup syslog + dvs.runcmd(['sh', '-c', "echo 0 > /var/log/syslog"]) + time.sleep(1) + print ("storm type: {} kbps value: {}".format(storm_type,kbps_value)) + #Add storm entry to config DB directly + self.add_storm_session(vlan_name,storm_type,kbps_value) + tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + (status,fvs) = tbl.get(vlan_name+"|"+storm_type) + assert status == True + assert len(fvs) > 0 + time.sleep(1) + #grep for error message in syslog + (exitcode,num) = dvs.runcmd(['sh', '-c', 'cat /var/log/syslog | grep -i "handlePortStormControlTable: {}: Unsupported / Invalid interface {}"'.format(storm_type,vlan_name)]) + time.sleep(1) + assert exitcode == 0 + self.delete_storm_session(vlan_name, storm_type) + self.remove_vlan_member(dvs,str(vlan_id),member_interface) + self.delete_vlan(dvs,str(vlan_id))