From 0f433515d1dc585848cfa58b38f2da65397f27bb Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Sat, 26 May 2018 06:06:48 +0800 Subject: [PATCH] [AclOrch] aclOrch enhancement to handle LAG port not configured case (#494) * enhance acl orch to handle the LAG port not configured case * rename variable, function; redunce redundant code; fix memory issue * revise according to comments * one more blank line * add new VS test cases for acl on LAG * update with PR comments fix * rename getValidPortId and move it to portOrch * fix indent * fix test case comments --- orchagent/aclorch.cpp | 154 +++++++++++---- orchagent/aclorch.h | 10 +- orchagent/main.cpp | 10 +- orchagent/orch.cpp | 2 +- orchagent/orchdaemon.cpp | 22 ++- orchagent/orchdaemon.h | 3 +- orchagent/portsorch.cpp | 39 ++++ orchagent/portsorch.h | 1 + tests/test_acl.py | 414 +++++++++++++++++++++++++++++++++++---- 9 files changed, 566 insertions(+), 89 deletions(-) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 7dc2bc2afe..4521723c95 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -936,7 +936,7 @@ bool AclTable::validate() { // Control plane ACLs are handled by a separate process if (type == ACL_TABLE_UNKNOWN || type == ACL_TABLE_CTRLPLANE) return false; - if (ports.empty()) return false; + if (portSet.empty()) return false; return true; } @@ -1365,8 +1365,8 @@ bool AclRange::remove() return true; } -AclOrch::AclOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : - Orch(db, tableNames), +AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : + Orch(connectors), m_mirrorOrch(mirrorOrch), m_neighOrch(neighOrch), m_routeOrch(routeOrch) @@ -1449,6 +1449,11 @@ void AclOrch::doTask(Consumer &consumer) unique_lock lock(m_countersMutex); doAclRuleTask(consumer); } + else if (table_name == STATE_LAG_TABLE_NAME) + { + unique_lock lock(m_countersMutex); + doAclTablePortUpdateTask(consumer); + } else { SWSS_LOG_ERROR("Invalid table %s", table_name.c_str()); @@ -1549,7 +1554,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) { KeyOpFieldsValuesTuple t = it->second; string key = kfvKey(t); - size_t found = key.find('|'); + size_t found = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); string table_id = key.substr(0, found); string op = kfvOp(t); @@ -1584,7 +1589,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) } else if (attr_name == TABLE_PORTS) { - bool suc = processPorts(attr_value, [&](sai_object_id_t portOid) { + bool suc = processPorts(newTable, attr_value, [&](sai_object_id_t portOid) { newTable.link(portOid); }); @@ -1649,7 +1654,7 @@ void AclOrch::doAclRuleTask(Consumer &consumer) { KeyOpFieldsValuesTuple t = it->second; string key = kfvKey(t); - size_t found = key.find('|'); + size_t found = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); string table_id = key.substr(0, found); string rule_id = key.substr(found + 1); string op = kfvOp(t); @@ -1729,17 +1734,79 @@ void AclOrch::doAclRuleTask(Consumer &consumer) } } -bool AclOrch::processPorts(string portsList, std::function inserter) +void AclOrch::doAclTablePortUpdateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + size_t found = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string port_alias = key.substr(0, found); + string op = kfvOp(t); + + SWSS_LOG_INFO("doAclTablePortUpdateTask: OP: %s, port_alias: %s", op.c_str(), port_alias.c_str()); + + if (op == SET_COMMAND) + { + for (auto itmap : m_AclTables) + { + auto table = itmap.second; + if (table.pendingPortSet.find(port_alias) != table.pendingPortSet.end()) + { + SWSS_LOG_INFO("found the port: %s in ACL table: %s pending port list, bind it to ACL table.", port_alias.c_str(), table.description.c_str()); + + bool suc = processPendingPort(table, port_alias, [&](sai_object_id_t portOid) { + table.link(portOid); + }); + + if (!suc) + { + SWSS_LOG_ERROR("Failed to bind the ACL table: %s to port: %s", table.description.c_str(), port_alias.c_str()); + } + else + { + table.pendingPortSet.erase(port_alias); + SWSS_LOG_DEBUG("port: %s bound to ACL table table: %s, remove it from pending list", port_alias.c_str(), table.description.c_str()); + } + } + } + } + else if (op == DEL_COMMAND) + { + for (auto itmap : m_AclTables) + { + auto table = itmap.second; + if (table.portSet.find(port_alias) != table.portSet.end()) + { + /*TODO: update the ACL table after port/lag deleted*/ + table.pendingPortSet.emplace(port_alias); + SWSS_LOG_INFO("Add deleted port: %s to the pending list of ACL table: %s", port_alias.c_str(), table.description.c_str()); + } + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +bool AclOrch::processPorts(AclTable &aclTable, string portsList, std::function inserter) { SWSS_LOG_ENTER(); vector strList; - SWSS_LOG_INFO("Processing ACL table port list %s", portsList.c_str()); + SWSS_LOG_DEBUG("Processing ACL table port list %s", portsList.c_str()); split(portsList, strList, ','); set strSet(strList.begin(), strList.end()); + aclTable.portSet = strSet; if (strList.size() != strSet.size()) { @@ -1755,33 +1822,52 @@ bool AclOrch::processPorts(string portsList, std::functiongetPort(alias, port)) { - SWSS_LOG_ERROR("Failed to process port. Port %s doesn't exist", alias.c_str()); - return false; + SWSS_LOG_INFO("Port %s not configured yet, add it to ACL table %s pending list", alias.c_str(), aclTable.description.c_str()); + aclTable.pendingPortSet.emplace(alias); + continue; } - switch (port.m_type) + if (gPortsOrch->getAclBindPortId(alias, port_id)) { - case Port::PHY: - if (port.m_lag_member_id != SAI_NULL_OBJECT_ID) - { - SWSS_LOG_ERROR("Failed to process port. Bind table to LAG member %s is not allowed", alias.c_str()); - return false; - } - inserter(port.m_port_id); - break; - case Port::LAG: - inserter(port.m_lag_id); - break; - case Port::VLAN: - inserter(port.m_vlan_info.vlan_oid); - break; - default: - SWSS_LOG_ERROR("Failed to process port. Incorrect port %s type %d", alias.c_str(), port.m_type); - return false; - } + inserter(port_id); + } + else + { + return false; + } + } + + return true; +} + +bool AclOrch::processPendingPort(AclTable &aclTable, string portAlias, std::function inserter) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("Processing ACL table port %s", portAlias.c_str()); + + sai_object_id_t port_id; + + Port port; + if (!gPortsOrch->getPort(portAlias, port)) + { + SWSS_LOG_INFO("Port %s not configured yet, add it to ACL table %s pending list", portAlias.c_str(), aclTable.description.c_str()); + aclTable.pendingPortSet.insert(portAlias); + return true; + } + + if (gPortsOrch->getAclBindPortId(portAlias, port_id)) + { + inserter(port_id); + aclTable.bind(port_id); + } + else + { + return false; } return true; @@ -1898,18 +1984,14 @@ sai_status_t AclOrch::bindAclTable(sai_object_id_t table_oid, AclTable &aclTable sai_status_t status = SAI_STATUS_SUCCESS; SWSS_LOG_INFO("%s table %s to ports", bind ? "Bind" : "Unbind", aclTable.id.c_str()); - + if (aclTable.ports.empty()) { if (bind) { - SWSS_LOG_ERROR("Port list is not configured for %s table", aclTable.id.c_str()); - return SAI_STATUS_FAILURE; - } - else - { - return SAI_STATUS_SUCCESS; + SWSS_LOG_WARN("Binding port list is empty for %s table", aclTable.id.c_str()); } + return SAI_STATUS_SUCCESS; } if (bind) diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index c96dbd8d30..31c8f3d356 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -248,6 +248,10 @@ class AclTable { std::map ports; // Map rule name to rule data map> rules; + // Set to store the ACL table port alias + set portSet; + // Set to store the not cofigured ACL table port alias + set pendingPortSet; AclTable() : type(ACL_TABLE_UNKNOWN) @@ -294,7 +298,7 @@ inline void split(string str, Iterable& out, char delim = ' ') class AclOrch : public Orch, public Observer { public: - AclOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); + AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); ~AclOrch(); void update(SubjectType, void *); @@ -319,6 +323,7 @@ class AclOrch : public Orch, public Observer void doTask(Consumer &consumer); void doAclTableTask(Consumer &consumer); void doAclRuleTask(Consumer &consumer); + void doAclTablePortUpdateTask(Consumer &consumer); void doTask(SelectableTimer &timer); static void collectCountersThread(AclOrch *pAclOrch); @@ -329,7 +334,8 @@ class AclOrch : public Orch, public Observer bool processAclTableType(string type, acl_table_type_t &table_type); bool processAclTableStage(string stage, acl_stage_type_t &acl_stage); - bool processPorts(string portsList, std::function inserter); + bool processPorts(AclTable &aclTable, string portsList, std::function inserter); + bool processPendingPort(AclTable &aclTable, string portAlias, std::function inserter); bool validateAclTable(AclTable &aclTable); //vector m_AclTables; diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 3a92a7ea8f..0ac7d1a72b 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -251,13 +251,15 @@ int main(int argc, char **argv) SWSS_LOG_NOTICE("Created underlay router interface ID %lx", gUnderlayIfId); /* Initialize orchestration components */ - DBConnector *appl_db = new DBConnector(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector *config_db = new DBConnector(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - OrchDaemon *orchDaemon = new OrchDaemon(appl_db, config_db); + OrchDaemon *orchDaemon = new OrchDaemon(&appl_db, &config_db, &state_db); if (!orchDaemon->init()) { SWSS_LOG_ERROR("Failed to initialize orchstration daemon"); + delete orchDaemon; exit(EXIT_FAILURE); } @@ -272,6 +274,7 @@ int main(int argc, char **argv) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to notify syncd APPLY_VIEW %d", status); + delete orchDaemon; exit(EXIT_FAILURE); } @@ -286,5 +289,6 @@ int main(int argc, char **argv) SWSS_LOG_ERROR("Failed due to exception: %s", e.what()); } + delete orchDaemon; return 0; } diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 81e213a168..49689a2d43 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -358,7 +358,7 @@ bool Orch::parseIndexRange(const string &input, sai_uint32_t &range_low, sai_uin void Orch::addConsumer(DBConnector *db, string tableName) { - if (db->getDB() == CONFIG_DB) + if (db->getDB() == CONFIG_DB || db->getDB() == STATE_DB) { addExecutor(tableName, new Consumer(new SubscriberStateTable(db, tableName), this)); } diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 3c9fe381e9..c3ca1458d9 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -27,9 +27,10 @@ RouteOrch *gRouteOrch; AclOrch *gAclOrch; CrmOrch *gCrmOrch; -OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb) : +OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb) : m_applDb(applDb), - m_configDb(configDb) + m_configDb(configDb), + m_stateDb(stateDb) { SWSS_LOG_ENTER(); } @@ -39,9 +40,6 @@ OrchDaemon::~OrchDaemon() SWSS_LOG_ENTER(); for (Orch *o : m_orchList) delete(o); - - delete(m_configDb); - delete(m_applDb); } bool OrchDaemon::init() @@ -97,11 +95,17 @@ bool OrchDaemon::init() MirrorOrch *mirror_orch = new MirrorOrch(appDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch); VRFOrch *vrf_orch = new VRFOrch(m_configDb, CFG_VRF_TABLE_NAME); - vector acl_tables = { - CFG_ACL_TABLE_NAME, - CFG_ACL_RULE_TABLE_NAME + TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_NAME); + TableConnector confDbAclRuleTable(m_configDb, CFG_ACL_RULE_TABLE_NAME); + TableConnector stateDbLagTable(m_stateDb, STATE_LAG_TABLE_NAME); + + vector acl_table_connectors = { + confDbAclTable, + confDbAclRuleTable, + stateDbLagTable }; - gAclOrch = new AclOrch(m_configDb, acl_tables, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); + + gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); m_orchList = { switch_orch, gCrmOrch, gPortsOrch, intfs_orch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, gAclOrch, gFdbOrch, vrf_orch }; m_select = new Select(); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index c7a96a2b0b..64e0b6cb73 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -28,7 +28,7 @@ using namespace swss; class OrchDaemon { public: - OrchDaemon(DBConnector *, DBConnector *); + OrchDaemon(DBConnector *, DBConnector *, DBConnector *); ~OrchDaemon(); bool init(); @@ -36,6 +36,7 @@ class OrchDaemon private: DBConnector *m_applDb; DBConnector *m_configDb; + DBConnector *m_stateDb; std::vector m_orchList; Select *m_select; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index abea052297..e0cf4b0fa6 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -394,6 +394,45 @@ bool PortsOrch::getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port return false; } +bool PortsOrch::getAclBindPortId(string alias, sai_object_id_t &port_id) +{ + SWSS_LOG_ENTER(); + + Port port; + if (getPort(alias, port)) + { + switch (port.m_type) + { + case Port::PHY: + if (port.m_lag_member_id != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_WARN("Invalid configuration. Bind table to LAG member %s is not allowed", alias.c_str()); + return false; + } + else + { + port_id = port.m_port_id; + } + break; + case Port::LAG: + port_id = port.m_lag_id; + break; + case Port::VLAN: + port_id = port.m_vlan_info.vlan_oid; + break; + default: + SWSS_LOG_ERROR("Failed to process port. Incorrect port %s type %d", alias.c_str(), port.m_type); + return false; + } + + return true; + } + else + { + return false; + } +} + void PortsOrch::setPort(string alias, Port p) { m_portList[alias] = p; diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index fa7ca42e50..52230feb04 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -53,6 +53,7 @@ class PortsOrch : public Orch, public Subject void setPort(string alias, Port port); void getCpuPort(Port &port); bool getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan); + bool getAclBindPortId(string alias, sai_object_id_t &port_id); bool setHostIntfsOperStatus(sai_object_id_t id, bool up); void updateDbPortOperStatus(sai_object_id_t id, sai_port_oper_status_t status); diff --git a/tests/test_acl.py b/tests/test_acl.py index dfcdd9492a..9a646c8f48 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -9,73 +9,97 @@ def get_acl_table_id(self, dvs, adb): keys = atbl.getKeys() assert dvs.asicdb.default_acl_table in keys acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_table] - + assert len(acl_tables) == 1 return acl_tables[0] - - def test_AclTableCreation(self, dvs): - - db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + def verify_if_any_acl_table_created(self, dvs, adb): + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + assert dvs.asicdb.default_acl_table in keys + acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_table] + + if (len(acl_tables) != 0): + return True + else: + return False + + def clean_up_left_over(self, dvs): adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - - bind_ports = ["Ethernet0", "Ethernet4"] - # create ACL_TABLE in config db - tbl = swsscommon.Table(db, "ACL_TABLE", '|') - fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) - tbl.set("test", fvs) - - time.sleep(1) - - # check acl table in asic db - test_acl_table_id = self.get_acl_table_id(dvs, adb) - - # check acl table group in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + keys = atbl.getKeys() + for key in keys: + atbl._del(key) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + keys = atbl.getKeys() + assert len(keys) == 0 + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") + keys = atbl.getKeys() + for key in keys: + atbl._del(key) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") + keys = atbl.getKeys() + assert len(keys) == 0 + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + keys = atbl.getKeys() + for key in keys: + atbl._del(key) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + keys = atbl.getKeys() + assert len(keys) == 0 + + def verify_acl_group_num(self, adb, expt): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") acl_table_groups = atbl.getKeys() - assert len(acl_table_groups) == 2 - + assert len(acl_table_groups) == expt + for k in acl_table_groups: (status, fvs) = atbl.get(k) assert status == True - for fv in fvs: if fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE": assert fv[1] == "SAI_ACL_STAGE_INGRESS" elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_BIND_POINT_TYPE_LIST": - assert fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_PORT" + assert (fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_PORT" or fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_LAG") elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_TYPE": assert fv[1] == "SAI_ACL_TABLE_GROUP_TYPE_PARALLEL" else: assert False - - # check acl table group member + + def verify_acl_group_member(self, adb, acl_group_ids, acl_table_id): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") keys = atbl.getKeys() - assert len(keys) == 2 - + member_groups = [] for k in keys: (status, fvs) = atbl.get(k) 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 acl_table_groups + assert fv[1] in acl_group_ids member_groups.append(fv[1]) elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": - assert fv[1] == test_acl_table_id + assert fv[1] == acl_table_id elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": assert True else: assert False - - assert set(member_groups) == set(acl_table_groups) - - # check port binding + + assert set(member_groups) == set(acl_group_ids) + + def verify_acl_port_binding(self, dvs, adb, bind_ports): + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_groups = atbl.getKeys() + assert len(acl_table_groups) == len(bind_ports) + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") - port_groups = [] for p in [dvs.asicdb.portnamemap[portname] for portname in bind_ports]: (status, fvs) = atbl.get(p) @@ -83,9 +107,55 @@ def test_AclTableCreation(self, dvs): if fv[0] == "SAI_PORT_ATTR_INGRESS_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) - + + def verify_acl_lag_binding(self, adb, lag_ids): + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_groups = atbl.getKeys() + assert len(acl_table_groups) == len(lag_ids) + + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + port_groups = [] + for lag_id in lag_ids: + (status, lagfvs) = atbl_lag.get(lag_id) + for lagfv in lagfvs: + if lagfv[0] == "SAI_LAG_ATTR_INGRESS_ACL": + assert lagfv[1] in acl_table_groups + port_groups.append(lagfv[1]) + + assert len(port_groups) == len(lag_ids) + assert set(port_groups) == set(acl_table_groups) + + def test_AclTableCreation(self, dvs): + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create ACL_TABLE in config db + bind_ports = ["Ethernet0", "Ethernet4"] + tbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test", fvs) + time.sleep(1) + + # check acl table in asic db + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table group in asic db + self.verify_acl_group_num(adb, 2) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 2 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # check port binding + self.verify_acl_port_binding(dvs, adb, bind_ports) + def test_AclRuleL4SrcPort(self, dvs): """ hmset ACL_RULE|test|acl_test_rule priority 55 PACKET_ACTION FORWARD L4_SRC_PORT 65000 @@ -152,7 +222,7 @@ def test_AclTableDeletion(self, dvs): keys = atbl.getKeys() # only the default table was left assert len(keys) == 1 - + def test_V6AclTableCreation(self, dvs): db = swsscommon.DBConnector(4, dvs.redis_sock, 0) @@ -779,3 +849,273 @@ def test_V6AclTableDeletion(self, dvs): keys = atbl.getKeys() # 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): + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + bind_ports = ["Ethernet0", "Ethernet4"] + # create ACL_TABLE in config db + tbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_insert", fvs) + + time.sleep(2) + + num_rules = 0 + #create ACL rules + tbl = swsscommon.Table(db, "ACL_RULE") + rules = [ [("PRIORITY", "10"), ("PACKET_ACTION", "DROP"), ("SRC_IP", "10.0.0.0/32")], + [("PRIORITY", "20"), ("PACKET_ACTION", "DROP"), ("DST_IP", "104.44.94.0/23")], + [("PRIORITY", "30"), ("PACKET_ACTION", "DROP"), ("DST_IP", "192.168.0.16/32")], + [("PRIORITY", "40"), ("PACKET_ACTION", "FORWARD"), ("DST_IP", "100.64.0.0/10")] ] + #used to verify how ACL rules are programmed in ASICDB + verifs = [ {'SAI_ACL_ENTRY_ATTR_PRIORITY': '10', + 'SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP': '10.0.0.0&mask:255.255.255.255', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_DROP'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '20', + 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '104.44.94.0&mask:255.255.254.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_DROP'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '30', + 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '192.168.0.16&mask:255.255.255.255', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_DROP'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '40', + 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '100.64.0.0&mask:255.192.0.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'} ] + #insert rules + for rule in rules: + fvs = swsscommon.FieldValuePairs(rule) + num_rules += 1 + tbl.set( "test_insert|acl_test_rule%s" % num_rules, fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + #assert that first set of rules are programmed + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == num_rules + + #insert new rule with odd priority + tbl = swsscommon.Table(db, "ACL_RULE") + insertrule = [("PRIORITY", "21"), ("PACKET_ACTION", "DROP"), ("ETHER_TYPE", "4660")] + #create verification for that rule + verifs.append({'SAI_ACL_ENTRY_ATTR_PRIORITY': '21', + 'SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE': '4660&mask:0xffff', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_DROP'}) + rules.append(insertrule) + fvs = swsscommon.FieldValuePairs(insertrule) + num_rules += 1 + tbl.set("test_insert|acl_test_rule%s" % num_rules, fvs) + + time.sleep(1) + + #assert all rules are programmed + keys = atbl.getKeys() + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == num_rules + + #match each entry to its corresponding verification + matched_rules = 0 + for entry in acl_entry: + (status, fvs) = atbl.get(entry) + assert status == True + assert len(fvs) == 6 + #helper function + if self.check_rule_existence(dict(fvs), rules, verifs): + matched_rules += 1 + + assert num_rules == matched_rules + + #cleanup + while num_rules > 0: + tbl._del("test_insert|acl_test_rule%s" % num_rules) + num_rules -= 1 + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test_insert") + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + # only the default table was left + assert len(keys) == 1 + + def test_AclTableCreationOnLAGMember(self, dvs): + # prepare db and tables + self.clean_up_left_over(dvs) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + apldb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + # create port channel + ps = swsscommon.ProducerStateTable(apldb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + ps.set("PortChannel0001", fvs) + + # create port channel member + ps = swsscommon.ProducerStateTable(apldb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + ps.set("PortChannel0001:Ethernet12", fvs) + time.sleep(1) + + # create acl table + tbl = swsscommon.Table(db, "ACL_TABLE") + bind_ports = ["Ethernet12"] + fvs = swsscommon.FieldValuePairs([("policy_desc", "test_negative"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_negative", fvs) + time.sleep(1) + + # verify test result - ACL table creation should fail + assert self.verify_if_any_acl_table_created(dvs, adb) == False + + def test_AclTableCreationOnLAG(self, dvs): + # prepare db and tables + self.clean_up_left_over(dvs) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + apldb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + #create port channel + ps = swsscommon.ProducerStateTable(apldb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + ps.set("PortChannel0002", fvs) + + # create port channel member + ps = swsscommon.ProducerStateTable(apldb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + ps.set("PortChannel0002:Ethernet16", fvs) + time.sleep(1) + + # create acl table + tbl = swsscommon.Table(db, "ACL_TABLE") + bind_ports = ["PortChannel0002"] + fvs = swsscommon.FieldValuePairs([("policy_desc", "test_negative"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_LAG", fvs) + time.sleep(1) + + # check acl table in asic db + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table group in asic db + self.verify_acl_group_num(adb, 1) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 1 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # get lad ids + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_ids = atbl_lag.getKeys(); + assert len(lag_ids) == 1 + + # check lag binding + self.verify_acl_lag_binding(adb, lag_ids) + + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test_LAG") + + def test_AclTableCreationBeforeLAG(self, dvs): + # prepare db and tables + self.clean_up_left_over(dvs) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + apldb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + # create acl table + tbl = swsscommon.Table(db, "ACL_TABLE") + bind_ports = ["PortChannel0003"] + fvs = swsscommon.FieldValuePairs([("policy_desc", "test_negative"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_LAG_2", fvs) + time.sleep(1) + + # check acl table in asic db + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table group in asic db + self.verify_acl_group_num(adb, 0) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 0 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # get lad ids + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_ids = atbl_lag.getKeys() + assert len(lag_ids) == 0 + + # check port binding + self.verify_acl_lag_binding(adb, lag_ids) + + # create port channel + ps = swsscommon.ProducerStateTable(apldb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + ps.set("PortChannel0003", fvs) + + # create port channel member + ps = swsscommon.ProducerStateTable(apldb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + ps.set("PortChannel0003:Ethernet20", fvs) + time.sleep(1) + + # notify aclorch that port channel configured + stdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(stdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("state", "ok")]) + ps.set("PortChannel0003", fvs) + time.sleep(1) + + # check acl table in asic db + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table group in asic db + self.verify_acl_group_num(adb, 1) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 1 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # get lad ids + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_ids = atbl_lag.getKeys() + assert len(lag_ids) == 1 + + # check port binding + self.verify_acl_lag_binding(adb, lag_ids) + + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test_LAG_2")