From 86b5e9970451aaa6c59271886e779f550710555a Mon Sep 17 00:00:00 2001 From: rupesh-k <53595165+rupesh-k@users.noreply.github.com> Date: Thu, 2 Jul 2020 00:32:40 +0530 Subject: [PATCH] [mirrororch] Port Mirroring implementation (#1314) Signed-off-by: Rupesh Kumar --- doc/swss-schema.md | 70 +++- orchagent/mirrororch.cpp | 401 +++++++++++++++++++---- orchagent/mirrororch.h | 17 + orchagent/portsorch.h | 2 +- tests/conftest.py | 36 +- tests/dvslib/dvs_acl.py | 11 + tests/dvslib/dvs_lag.py | 10 +- tests/dvslib/dvs_mirror.py | 100 ++++++ tests/dvslib/dvs_policer.py | 19 ++ tests/test_mirror_port_erspan.py | 546 +++++++++++++++++++++++++++++++ tests/test_mirror_port_span.py | 470 ++++++++++++++++++++++++++ 11 files changed, 1616 insertions(+), 66 deletions(-) create mode 100644 tests/dvslib/dvs_mirror.py create mode 100644 tests/dvslib/dvs_policer.py create mode 100644 tests/test_mirror_port_erspan.py create mode 100644 tests/test_mirror_port_span.py diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 8cb191a2311a..7ac0c24d3a3a 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -595,6 +595,10 @@ Stores information about mirror sessions and their properties. ttl = h8 ; Session TTL queue = h8 ; Session output queue policer = policer_name ; Session policer name + dst_port = PORT_TABLE|ifname ; Session destination PORT + src_port = PORT_TABLE|ifname ; Session source PORT/LAG list + direction = "RX"/"TX"/"BOTH" ; Session direction + type = "SPAN"/"ERSPAN" ; Session type. Default is ERSPAN ;value annotations mirror_session_name = 1*255VCHAR @@ -623,6 +627,37 @@ Example: } ] + [ + { + "MIRROR_SESSION_TABLE:session_2": { + "src_ip": "1.1.1.1", + "dst_ip": "2.2.2.2", + "gre_type": "0x6558", + "dscp": "50", + "ttl": "64", + "queue": "0" + "src_port": "Ethernet0,PortChannel001" + "direction": "BOTH" + "type": "ERSPAN" + }, + "OP": "SET" + } + ] + + [ + { + "MIRROR_SESSION_TABLE:session_3": { + "type": "SPAN" + "dst_port": "Ethernet0" + "src_port": "Ethernet4,PortChannel002" + "direction": "BOTH" + }, + "OP": "SET" + } + ] + + + Equivalent RedisDB entry: 127.0.0.1:6379> KEYS *MIRROR* @@ -631,7 +666,7 @@ Equivalent RedisDB entry: 1) "src_ip" 2) "1.1.1.1" 3) "dst_ip" - 4) "2.2.2.2 + 4) "2.2.2.2" 5) "gre_type" 6) "0x6558" 7) "dscp" @@ -641,6 +676,39 @@ Equivalent RedisDB entry: 11) "queue" 12) "0" + 127.0.0.1:6379> KEYS *MIRROR* + 1) "MIRROR_SESSION_TABLE:session_2" + 127.0.0.1:6379> HGETALL MIRROR_SESSION_TABLE:session_2 + 1) "src_ip" + 2) "1.1.1.1" + 3) "dst_ip" + 4) "2.2.2.2" + 5) "gre_type" + 6) "0x6558" + 7) "dscp" + 8) "50" + 9) "ttl" + 10) "64" + 11) "queue" + 12) "0" + 13) "src_port" + 14) "Ethernet0,PortChannel001" + 15) "direction" + 16) "BOTH" + 17) "type" + 18) "ERSPAN" + + 127.0.0.1:6379> KEYS *MIRROR* + 1) "MIRROR_SESSION_TABLE:session_1" + 127.0.0.1:6379> HGETALL MIRROR_SESSION_TABLE:session_3i + 1) "type" + 2) "SPAN" + 3) "dst_port" + 4) "Ethernet0" + 5) "src_port" + 6) "Ethernet4,PortChannel002" + 7) "direction" + 8) "RX" --------------------------------------------- ### POLICER_TABLE diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 0de90a424dd4..4eac56ee7a56 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -27,6 +27,10 @@ #define MIRROR_SESSION_ROUTE_PREFIX "route_prefix" #define MIRROR_SESSION_VLAN_ID "vlan_id" #define MIRROR_SESSION_POLICER "policer" +#define MIRROR_SESSION_SRC_PORT "src_port" +#define MIRROR_SESSION_DST_PORT "dst_port" +#define MIRROR_SESSION_DIRECTION "direction" +#define MIRROR_SESSION_TYPE "type" #define MIRROR_SESSION_DEFAULT_VLAN_PRI 0 #define MIRROR_SESSION_DEFAULT_VLAN_CFI 0 @@ -36,6 +40,7 @@ #define MIRROR_SESSION_DSCP_MAX 63 extern sai_mirror_api_t *sai_mirror_api; +extern sai_port_api_t *sai_port_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; @@ -123,7 +128,7 @@ bool MirrorOrch::bake() key, monitor_port + state_db_key_delimiter + next_hop_ip); } - removeSessionState(key); + removeSessionState(key); } return Orch::bake(); @@ -260,6 +265,81 @@ bool MirrorOrch::decreaseRefCount(const string& name) return true; } +bool MirrorOrch::validateDstPort(const string& dstPort) +{ + Port port; + if (!m_portsOrch->getPort(dstPort, port)) + { + SWSS_LOG_ERROR("Not supported port %s type %d", dstPort.c_str(), port.m_type); + return false; + } + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("Not supported port %s", dstPort.c_str()); + return false; + } + return true; +} + +bool MirrorOrch::checkPortExistsInSrcPortList(const string& port, const string& srcPortList) +{ + auto ports = tokenize(srcPortList, ','); + if (ports.size() != 0) + { + for (auto alias : ports) + { + if(port == alias) + { + return true; + } + } + } + + return false; +} + +bool MirrorOrch::validateSrcPortList(const string& srcPortList) +{ + auto ports = tokenize(srcPortList, ','); + + if (ports.size() != 0) + { + for (auto alias : ports) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Failed to locate Port/LAG %s", alias.c_str()); + return false; + } + + if(!(port.m_type == Port::PHY || port.m_type == Port::LAG)) + { + SWSS_LOG_ERROR("Not supported port %s", alias.c_str()); + return false; + } + + // Check if the ports in LAG are part of source port list + if (port.m_type == Port::LAG) + { + vector portv; + m_portsOrch->getLagMember(port, portv); + for (const auto p : portv) + { + if (checkPortExistsInSrcPortList(p.m_alias, srcPortList)) + { + SWSS_LOG_ERROR("Port %s in LAG %s is also part of src_port config %s", + p.m_alias.c_str(), port.m_alias.c_str(), srcPortList.c_str()); + return false; + } + } + } + } + } + + return true; +} + void MirrorOrch::createEntry(const string& key, const vector& data) { SWSS_LOG_ENTER(); @@ -283,7 +363,7 @@ void MirrorOrch::createEntry(const string& key, const vector& d entry.srcIp = fvValue(i); if (!entry.srcIp.isV4()) { - SWSS_LOG_ERROR("Unsupported version of sessions %s source IP address\n", key.c_str()); + SWSS_LOG_ERROR("Unsupported version of sessions %s source IP address", key.c_str()); return; } } @@ -292,7 +372,7 @@ void MirrorOrch::createEntry(const string& key, const vector& d entry.dstIp = fvValue(i); if (!entry.dstIp.isV4()) { - SWSS_LOG_ERROR("Unsupported version of sessions %s destination IP address\n", key.c_str()); + SWSS_LOG_ERROR("Unsupported version of sessions %s destination IP address", key.c_str()); return; } } @@ -324,9 +404,41 @@ void MirrorOrch::createEntry(const string& key, const vector& d m_policerOrch->increaseRefCount(fvValue(i)); entry.policer = fvValue(i); } + else if (fvField(i) == MIRROR_SESSION_SRC_PORT) + { + if (!validateSrcPortList(fvValue(i))) + { + SWSS_LOG_ERROR("Failed to get valid source port list %s", fvValue(i).c_str()); + return; + } + entry.src_port = fvValue(i); + } + else if (fvField(i) == MIRROR_SESSION_DST_PORT) + { + if (!validateDstPort(fvValue(i))) + { + SWSS_LOG_ERROR("Failed to get valid destination port %s", fvValue(i).c_str()); + return; + } + entry.dst_port = fvValue(i); + } + else if (fvField(i) == MIRROR_SESSION_DIRECTION) + { + if (!(fvValue(i) == MIRROR_RX_DIRECTION || fvValue(i) == MIRROR_TX_DIRECTION + || fvValue(i) == MIRROR_BOTH_DIRECTION)) + { + SWSS_LOG_ERROR("Failed to get valid direction %s", fvValue(i).c_str()); + return; + } + entry.direction = fvValue(i); + } + else if (fvField(i) == MIRROR_SESSION_TYPE) + { + entry.type = fvValue(i); + } else { - SWSS_LOG_ERROR("Failed to parse session %s configuration. Unknown attribute %s.\n", key.c_str(), fvField(i).c_str()); + SWSS_LOG_ERROR("Failed to parse session %s configuration. Unknown attribute %s", key.c_str(), fvField(i).c_str()); return; } } @@ -348,8 +460,16 @@ void MirrorOrch::createEntry(const string& key, const vector& d setSessionState(key, entry); - // Attach the destination IP to the routeOrch - m_routeOrch->attach(this, entry.dstIp); + if (entry.type == MIRROR_SESSION_SPAN && !entry.dst_port.empty()) + { + auto &session1 = m_syncdMirrors.find(key)->second; + activateSession(key, session1); + } + else + { + // Attach the destination IP to the routeOrch + m_routeOrch->attach(this, entry.dstIp); + } } task_process_status MirrorOrch::deleteEntry(const string& name) @@ -375,7 +495,10 @@ task_process_status MirrorOrch::deleteEntry(const string& name) if (session.status) { - m_routeOrch->detach(this, session.dstIp); + if (session.type != MIRROR_SESSION_SPAN) + { + m_routeOrch->detach(this, session.dstIp); + } if (!deactivateSession(name, session)) { SWSS_LOG_ERROR("Failed to remove mirror session %s", name.c_str()); @@ -613,6 +736,97 @@ bool MirrorOrch::updateSession(const string& name, MirrorEntry& session) return ret; } +bool MirrorOrch::setUnsetPortMirror(Port port, + bool ingress, + bool set, + sai_object_id_t sessionId) +{ + sai_status_t status; + sai_attribute_t port_attr; + port_attr.id = ingress ? SAI_PORT_ATTR_INGRESS_MIRROR_SESSION: + SAI_PORT_ATTR_EGRESS_MIRROR_SESSION; + if (set) + { + port_attr.value.objlist.count = 1; + port_attr.value.objlist.list = (long unsigned int *)calloc(port_attr.value.objlist.count, sizeof(sai_object_id_t)); + port_attr.value.objlist.list[0] = sessionId; + } + else + { + port_attr.value.objlist.count = 0; + } + + if (port.m_type == Port::LAG) + { + vector portv; + m_portsOrch->getLagMember(port, portv); + for (const auto p : portv) + { + if (p.m_type != Port::PHY) + { + SWSS_LOG_ERROR("Failed to locate port %s", p.m_alias.c_str()); + return false; + } + status = sai_port_api->set_port_attribute(p.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to configure %s session on port %s: %s, status %d, sessionId %x", + ingress ? "RX" : "TX", port.m_alias.c_str(), + p.m_alias.c_str(), status, sessionId); + return false; + } + } + } + else if (port.m_type == Port::PHY) + { + status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to configure %s session on port %s, status %d, sessionId %x", + ingress ? "RX" : "TX", port.m_alias.c_str(), status, sessionId); + return false; + } + } + return true; +} + +bool MirrorOrch::configurePortMirrorSession(const string& name, MirrorEntry& session, bool set) +{ + auto ports = tokenize(session.src_port, ','); + if (ports.size() != 0) + { + for (auto alias : ports) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Failed to locate port/LAG %s", alias.c_str()); + return false; + } + if (session.direction == MIRROR_RX_DIRECTION || session.direction == MIRROR_BOTH_DIRECTION) + { + if (!setUnsetPortMirror(port, true, set, session.sessionId)) + { + SWSS_LOG_ERROR("Failed to configure mirror session %s port %s", + name.c_str(), port.m_alias.c_str()); + return false; + } + } + if (session.direction == MIRROR_TX_DIRECTION || session.direction == MIRROR_BOTH_DIRECTION) + { + if (!setUnsetPortMirror(port, false, set, session.sessionId)) + { + SWSS_LOG_ERROR("Failed to configure mirror session %s port %s", + name.c_str(), port.m_alias.c_str()); + return false; + } + } + } + } + + return true; +} + bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) { SWSS_LOG_ENTER(); @@ -632,75 +846,95 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) attrs.push_back(attr); } - attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; - attr.value.oid = session.neighborInfo.portId; - attrs.push_back(attr); + if (session.type == MIRROR_SESSION_SPAN) + { + Port dst_port; + if (!m_portsOrch->getPort(session.dst_port, dst_port)) + { + SWSS_LOG_ERROR("Failed to locate Port/LAG %s", session.dst_port.c_str()); + return false; + } - attr.id = SAI_MIRROR_SESSION_ATTR_TYPE; - attr.value.s32 = SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE; - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; + attr.value.oid = dst_port.m_port_id; + attrs.push_back(attr); - // Add the VLAN header when the packet is sent out from a VLAN - if (session.neighborInfo.port.m_type == Port::VLAN) + attr.id = SAI_MIRROR_SESSION_ATTR_TYPE; + attr.value.oid = SAI_MIRROR_SESSION_TYPE_LOCAL; + attrs.push_back(attr); + } + else { - attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID; - attr.value.booldata = true; + attr.id = SAI_MIRROR_SESSION_ATTR_MONITOR_PORT; + attr.value.oid = session.neighborInfo.portId; attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_TPID; - attr.value.u16 = ETH_P_8021Q; + attr.id = SAI_MIRROR_SESSION_ATTR_TYPE; + attr.value.s32 = SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE; attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_ID; - attr.value.u16 = session.neighborInfo.port.m_vlan_info.vlan_id; - attrs.push_back(attr); + // Add the VLAN header when the packet is sent out from a VLAN + if (session.neighborInfo.port.m_type == Port::VLAN) + { + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID; + attr.value.booldata = true; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_PRI; - attr.value.u8 = MIRROR_SESSION_DEFAULT_VLAN_PRI; - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_TPID; + attr.value.u16 = ETH_P_8021Q; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_CFI; - attr.value.u8 = MIRROR_SESSION_DEFAULT_VLAN_CFI; - attrs.push_back(attr); - } + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_ID; + attr.value.u16 = session.neighborInfo.port.m_vlan_info.vlan_id; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_ERSPAN_ENCAPSULATION_TYPE; - attr.value.s32 = SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL; - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_PRI; + attr.value.u8 = MIRROR_SESSION_DEFAULT_VLAN_PRI; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION; - attr.value.u8 = MIRROR_SESSION_DEFAULT_IP_HDR_VER; - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_VLAN_CFI; + attr.value.u8 = MIRROR_SESSION_DEFAULT_VLAN_CFI; + attrs.push_back(attr); + } - // TOS value format is the following: - // DSCP 6 bits | ECN 2 bits - attr.id = SAI_MIRROR_SESSION_ATTR_TOS; - attr.value.u16 = (uint16_t)(session.dscp << MIRROR_SESSION_DSCP_SHIFT); - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_ERSPAN_ENCAPSULATION_TYPE; + attr.value.s32 = SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_TTL; - attr.value.u8 = session.ttl; - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION; + attr.value.u8 = MIRROR_SESSION_DEFAULT_IP_HDR_VER; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS; - copy(attr.value.ipaddr, session.srcIp); - attrs.push_back(attr); + // TOS value format is the following: + // DSCP 6 bits | ECN 2 bits + attr.id = SAI_MIRROR_SESSION_ATTR_TOS; + attr.value.u16 = (uint16_t)(session.dscp << MIRROR_SESSION_DSCP_SHIFT); + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS; - copy(attr.value.ipaddr, session.dstIp); - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_TTL; + attr.value.u8 = session.ttl; + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS; - memcpy(attr.value.mac, gMacAddress.getMac(), sizeof(sai_mac_t)); - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS; + copy(attr.value.ipaddr, session.srcIp); + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS; - memcpy(attr.value.mac, session.neighborInfo.mac.getMac(), sizeof(sai_mac_t)); - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS; + copy(attr.value.ipaddr, session.dstIp); + attrs.push_back(attr); - attr.id = SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE; - attr.value.u16 = session.greType; - attrs.push_back(attr); + attr.id = SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS; + memcpy(attr.value.mac, gMacAddress.getMac(), sizeof(sai_mac_t)); + attrs.push_back(attr); + + attr.id = SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS; + memcpy(attr.value.mac, session.neighborInfo.mac.getMac(), sizeof(sai_mac_t)); + attrs.push_back(attr); + + attr.id = SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE; + attr.value.u16 = session.greType; + attrs.push_back(attr); + } if (!session.policer.empty()) { @@ -720,13 +954,25 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) create_mirror_session(&session.sessionId, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to activate mirroring session %s\n", name.c_str()); + SWSS_LOG_ERROR("Failed to activate mirroring session %s", name.c_str()); session.status = false; return false; } session.status = true; + + if (!session.src_port.empty() && !session.direction.empty()) + { + status = configurePortMirrorSession(name, session, true); + if (status == false) + { + SWSS_LOG_ERROR("Failed to activate port mirror session %s", name.c_str()); + session.status = false; + return false; + } + } + setSessionState(name, session); MirrorSessionUpdate update = { name, true }; @@ -740,17 +986,28 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) bool MirrorOrch::deactivateSession(const string& name, MirrorEntry& session) { SWSS_LOG_ENTER(); + sai_status_t status; assert(session.status); MirrorSessionUpdate update = { name, false }; notify(SUBJECT_TYPE_MIRROR_SESSION_CHANGE, static_cast(&update)); - sai_status_t status = sai_mirror_api-> - remove_mirror_session(session.sessionId); + if (!session.src_port.empty() && !session.direction.empty()) + { + status = configurePortMirrorSession(name, session, false); + if (status == false) + { + SWSS_LOG_ERROR("Failed to deactivate port mirror session %s", name.c_str()); + return false; + } + } + + status = sai_mirror_api->remove_mirror_session(session.sessionId); + if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to deactivate mirroring session %s\n", name.c_str()); + SWSS_LOG_ERROR("Failed to deactivate mirroring session %s", name.c_str()); return false; } @@ -1044,6 +1301,26 @@ void MirrorOrch::updateLagMember(const LagMemberUpdate& update) const auto& name = it->first; auto& session = it->second; + // Check the following conditions: + // 1) Session is active + // 2) LAG is part of mirror session source ports. + // 3) Member port is not part of session source ports. + // if the above condition matches then set/unset mirror configuration to new member port. + if (session.status && + !session.src_port.empty() && + session.src_port.find(update.lag.m_alias.c_str()) != std::string::npos && + !checkPortExistsInSrcPortList(update.member.m_alias, session.src_port)) + { + if (session.direction == MIRROR_RX_DIRECTION || session.direction == MIRROR_BOTH_DIRECTION) + { + setUnsetPortMirror(update.member, true, update.add, session.sessionId); + } + if (session.direction == MIRROR_TX_DIRECTION || session.direction == MIRROR_BOTH_DIRECTION) + { + setUnsetPortMirror(update.member, false, update.add, session.sessionId); + } + } + // Check the following two conditions: // 1) the neighbor is LAG // 2) the neighbor LAG matches the update LAG diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index 798eb4583a0a..5edbf0ee456d 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -18,6 +18,12 @@ #include #include +#define MIRROR_RX_DIRECTION "RX" +#define MIRROR_TX_DIRECTION "TX" +#define MIRROR_BOTH_DIRECTION "BOTH" +#define MIRROR_SESSION_SPAN "SPAN" +#define MIRROR_SESSION_ERSPAN "ERSPAN" + /* * Contains session data specified by user in config file * and data required for MAC address and port resolution @@ -32,6 +38,10 @@ struct MirrorEntry uint8_t ttl; uint8_t queue; string policer; + string dst_port; + string src_port; + string direction; + string type; struct { @@ -118,6 +128,13 @@ class MirrorOrch : public Orch, public Observer, public Subject void updateLagMember(const LagMemberUpdate&); void updateVlanMember(const VlanMemberUpdate&); + bool checkPortExistsInSrcPortList(const string& port, const string& srcPortList); + bool validateSrcPortList(const string& srcPort); + bool validateDstPort(const string& dstPort); + bool setUnsetPortMirror(Port port, bool ingress, bool set, + sai_object_id_t sessionId); + bool configurePortMirrorSession(const string&, MirrorEntry&, bool enable); + void doTask(Consumer& consumer); }; diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 5b7662470a89..62584da2507d 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -121,6 +121,7 @@ class PortsOrch : public Orch, public Subject bool addSubPort(Port &port, const string &alias, const bool &adminUp = true, const uint32_t &mtu = 0); bool removeSubPort(const string &alias); + void getLagMember(Port &lag, vector &portv); private: unique_ptr m_counterTable; unique_ptr
m_counterLagTable; @@ -206,7 +207,6 @@ class PortsOrch : public Orch, public Subject bool removeLagMember(Port &lag, Port &port); bool setCollectionOnLagMember(Port &lagMember, bool enableCollection); bool setDistributionOnLagMember(Port &lagMember, bool enableDistribution); - void getLagMember(Port &lag, vector &portv); bool addPort(const set &lane_set, uint32_t speed, int an=0, string fec=""); sai_status_t removePort(sai_object_id_t port_id); diff --git a/tests/conftest.py b/tests/conftest.py index fac32b321768..c8d676f8b79f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,8 @@ from dvslib import dvs_acl from dvslib import dvs_vlan from dvslib import dvs_lag +from dvslib import dvs_mirror +from dvslib import dvs_policer def ensure_system(cmd): (rc, output) = commands.getstatusoutput(cmd) @@ -816,6 +818,26 @@ def remove_neighbor(self, interface, ip): tbl._del(interface + ":" + ip) time.sleep(1) + def add_route(self, prefix, nexthop): + self.runcmd("ip route add " + prefix + " via " + nexthop) + time.sleep(1) + + def remove_route(self, prefix): + self.runcmd("ip route del " + prefix) + time.sleep(1) + + def create_fdb(self, vlan, mac, interface): + tbl = swsscommon.ProducerStateTable(self.pdb, "FDB_TABLE") + fvs = swsscommon.FieldValuePairs([("port", interface), + ("type", "dynamic")]) + tbl.set("Vlan" + vlan + ":" + mac, fvs) + time.sleep(1) + + def remove_fdb(self, vlan, mac): + tbl = swsscommon.ProducerStateTable(self.pdb, "FDB_TABLE") + tbl._del("Vlan" + vlan + ":" + mac) + time.sleep(1) + def setup_db(self): self.pdb = swsscommon.DBConnector(0, self.redis_sock, 0) self.adb = swsscommon.DBConnector(1, self.redis_sock, 0) @@ -1014,7 +1036,8 @@ def dvs_acl_manager(request, dvs): @pytest.yield_fixture(scope="class") def dvs_lag_manager(request, dvs): - request.cls.dvs_lag = dvs_lag.DVSLag(dvs.get_config_db()) + request.cls.dvs_lag = dvs_lag.DVSLag(dvs.get_asic_db(), + dvs.get_config_db()) @pytest.yield_fixture(scope="class") def dvs_vlan_manager(request, dvs): @@ -1023,6 +1046,17 @@ def dvs_vlan_manager(request, dvs): dvs.get_state_db(), dvs.get_counters_db(), dvs.get_app_db()) +@pytest.yield_fixture(scope="class") +def dvs_mirror_manager(request, dvs): + request.cls.dvs_mirror = dvs_mirror.DVSMirror(dvs.get_asic_db(), + dvs.get_config_db(), + dvs.get_state_db(), + dvs.get_counters_db(), + dvs.get_app_db()) +@pytest.yield_fixture(scope="class") +def dvs_policer_manager(request, dvs): + request.cls.dvs_policer = dvs_policer.DVSPolicer(dvs.get_asic_db(), + dvs.get_config_db()) ##################### DPB fixtures ########################################### def create_dpb_config_file(dvs): cmd = "sonic-cfggen -j /etc/sonic/init_cfg.json -j /tmp/ports.json --print-data > /tmp/dpb_config_db.json" diff --git a/tests/dvslib/dvs_acl.py b/tests/dvslib/dvs_acl.py index e859b23aaee1..3f5576200985 100644 --- a/tests/dvslib/dvs_acl.py +++ b/tests/dvslib/dvs_acl.py @@ -136,6 +136,16 @@ def create_acl_rule(self, table_name, rule_name, qualifiers, action="FORWARD", p self.config_db.create_entry("ACL_RULE", "{}|{}".format(table_name, rule_name), fvs) + def create_mirror_acl_rule(self, table_name, rule_name, qualifiers, priority="2020"): + fvs = { + "priority": priority + } + + for k, v in qualifiers.items(): + fvs[k] = v + + self.config_db.create_entry("ACL_RULE", "{}|{}".format(table_name, rule_name), fvs) + def remove_acl_rule(self, table_name, rule_name): self.config_db.delete_entry("ACL_RULE", "{}|{}".format(table_name, rule_name)) @@ -229,3 +239,4 @@ def _match_acl_range(sai_acl_range): return True return _match_acl_range + diff --git a/tests/dvslib/dvs_lag.py b/tests/dvslib/dvs_lag.py index 6eafb084f821..06dd0c421783 100644 --- a/tests/dvslib/dvs_lag.py +++ b/tests/dvslib/dvs_lag.py @@ -1,5 +1,6 @@ class DVSLag(object): - def __init__(self, cdb): + def __init__(self, adb, cdb): + self.asic_db = adb self.config_db = cdb def create_port_channel(self, lag_id, admin_status="up", mtu="1500"): @@ -19,3 +20,10 @@ def create_port_channel_member(self, lag_id, interface): def remove_port_channel_member(self, lag_id, interface): member = "PortChannel{}|{}".format(lag_id, interface) self.config_db.delete_entry("PORTCHANNEL_MEMBER", member) + + def get_and_verify_port_channel_members(self, expected_num): + return self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER", expected_num) + + def get_and_verify_port_channel(self, expected_num): + return self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", expected_num) + diff --git a/tests/dvslib/dvs_mirror.py b/tests/dvslib/dvs_mirror.py new file mode 100644 index 000000000000..6529b1a72657 --- /dev/null +++ b/tests/dvslib/dvs_mirror.py @@ -0,0 +1,100 @@ +class DVSMirror(object): + def __init__(self, adb, cdb, sdb, cntrdb, appdb): + self.asic_db = adb + self.config_db = cdb + self.state_db = sdb + self.counters_db = cntrdb + self.app_db = appdb + + def create_span_session(self, name, dst_port, src_ports=None, direction="BOTH", queue=None, policer=None): + mirror_entry = {"type": "SPAN"} + if dst_port: + mirror_entry["dst_port"] = dst_port + + if src_ports: + mirror_entry["src_port"] = src_ports + # set direction without source port to uncover any swss issues. + mirror_entry["direction"] = direction + + if queue: + mirror_entry["queue"] = queue + if policer: + mirror_entry["policer"] = policer + self.config_db.create_entry("MIRROR_SESSION", name, mirror_entry) + + def create_erspan_session(self, name, src, dst, gre, dscp, ttl, queue, policer=None, src_ports=None, direction="BOTH"): + mirror_entry = {} + mirror_entry["src_ip"] = src + mirror_entry["dst_ip"] = dst + mirror_entry["gre_type"] = gre + mirror_entry["dscp"] = dscp + mirror_entry["ttl"] = ttl + mirror_entry["queue"] = queue + if policer: + mirror_entry["policer"] = policer + if src_ports: + mirror_entry["src_port"] = src_ports + mirror_entry["direction"] = direction + + self.config_db.create_entry("MIRROR_SESSION", name, mirror_entry) + + def remove_mirror_session(self, name): + self.config_db.delete_entry("MIRROR_SESSION", name) + + def verify_no_mirror(self): + self.config_db.wait_for_n_keys("MIRROR_SESSION", 0) + self.state_db.wait_for_n_keys("MIRROR_SESSION_TABLE", 0) + + def verify_session_status(self, name, status="active", expected=1): + self.state_db.wait_for_n_keys("MIRROR_SESSION_TABLE", expected) + if expected: + self.state_db.wait_for_field_match("MIRROR_SESSION_TABLE", name, {"status": status}) + + def verify_port_mirror_config(self, dvs, ports, direction, session_oid="null"): + fvs = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + fvs = dict(fvs) + for p in ports: + port_oid = fvs.get(p) + member = dvs.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid) + if direction in {"RX", "BOTH"}: + assert member["SAI_PORT_ATTR_INGRESS_MIRROR_SESSION"] == "1:"+session_oid + else: + assert "SAI_PORT_ATTR_INGRESS_MIRROR_SESSION" not in member.keys() or member["SAI_PORT_ATTR_INGRESS_MIRROR_SESSION"] == "0:null" + if direction in {"TX", "BOTH"}: + assert member["SAI_PORT_ATTR_EGRESS_MIRROR_SESSION"] == "1:"+session_oid + else: + assert "SAI_PORT_ATTR_EGRESS_MIRROR_SESSION" not in member.keys() or member["SAI_PORT_ATTR_EGRESS_MIRROR_SESSION"] == "0:null" + + def verify_session_db(self, dvs, name, asic_table=None, asic=None, state=None, asic_size=None): + if asic: + fv_pairs = dvs.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION", asic_table) + assert all(fv_pairs.get(k) == v for k, v in asic.items()) + if asic_size: + assert asic_size == len(fv_pairs) + if state: + fv_pairs = dvs.state_db.wait_for_entry("MIRROR_SESSION_TABLE", name) + assert all(fv_pairs.get(k) == v for k, v in state.items()) + + def verify_session_policer(self, dvs, policer_oid, cir): + if cir: + entry = dvs.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_POLICER", policer_oid) + assert entry["SAI_POLICER_ATTR_CIR"] == cir + + def verify_session(self, dvs, name, asic_db=None, state_db=None, dst_oid=None, src_ports=None, direction="BOTH", policer=None, expected = 1, asic_size=None): + member_ids = self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION", expected) + session_oid=member_ids[0] + # with multiple sessions, match on dst_oid to get session_oid + if dst_oid: + for member in member_ids: + entry=dvs.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION", member) + if entry["SAI_MIRROR_SESSION_ATTR_MONITOR_PORT"] == dst_oid: + session_oid = member + + self.verify_session_db(dvs, name, session_oid, asic=asic_db, state=state_db, asic_size=asic_size) + if policer: + cir = dvs.config_db.wait_for_entry("POLICER", policer)["cir"] + entry=dvs.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION", session_oid) + self.verify_session_policer(dvs, entry["SAI_MIRROR_SESSION_ATTR_POLICER"], cir) + if src_ports: + self.verify_port_mirror_config(dvs, src_ports, direction, session_oid=session_oid) + diff --git a/tests/dvslib/dvs_policer.py b/tests/dvslib/dvs_policer.py new file mode 100644 index 000000000000..5aa839a86a96 --- /dev/null +++ b/tests/dvslib/dvs_policer.py @@ -0,0 +1,19 @@ +class DVSPolicer(object): + def __init__(self, adb, cdb): + self.asic_db = adb + self.config_db = cdb + + def create_policer(self, name, type="packets", cir="600", cbs="600", mode="sr_tcm", red_action="drop" ): + policer_entry = {"meter_type": type, "mode": mode, + "cir": cir, "cbs": cbs, "red_packet_action": red_action} + self.config_db.create_entry("POLICER", name, policer_entry) + + def remove_policer(self, name): + self.config_db.delete_entry("POLICER", name) + + def verify_policer(self, name, expected=1): + self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_POLICER", expected) + + def verify_no_policer(self): + self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY", 0) + diff --git a/tests/test_mirror_port_erspan.py b/tests/test_mirror_port_erspan.py new file mode 100644 index 000000000000..47d9d5d1ca64 --- /dev/null +++ b/tests/test_mirror_port_erspan.py @@ -0,0 +1,546 @@ +# This test suite covers the functionality of mirror feature in SwSS +import pytest + +@pytest.mark.usefixtures("testlog") +@pytest.mark.usefixtures('dvs_vlan_manager') +@pytest.mark.usefixtures('dvs_lag_manager') +@pytest.mark.usefixtures('dvs_mirror_manager') + +class TestMirror(object): + def test_PortMirrorERSpanAddRemove(self, dvs, testlog): + """ + This test covers the basic ERSPANmirror session creation and removal operations + Operation flow: + 1. Create mirror session with source ports. + The session remains inactive because no nexthop/neighbor exists + 2. Bring up port; assign IP; create neighbor; create route + The session remains inactive until the route is created + 3. Verify that port mirror config is proper. + 4. Remove route; remove neighbor; remove IP; bring down port + The session becomes inactive again till the end + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + src_ports = "Ethernet12" + src_asic_ports = ["Ethernet12"] + + # create mirror session + self.dvs_mirror.create_erspan_session(session, "1.1.1.1", "2.2.2.2", "0x6558", "8", "100", "0", None, src_ports) + self.dvs_mirror.verify_session_status(session, status="inactive") + + # bring up Ethernet16 + dvs.set_interface_status("Ethernet16", "up") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # add IP address to Ethernet16 + dvs.add_ip_address("Ethernet16", "10.0.0.0/31") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # add neighbor to Ethernet16 + dvs.add_neighbor("Ethernet16", "10.0.0.1", "02:04:06:08:10:12") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # add route to mirror destination via 10.0.0.1 + dvs.add_route("2.2.2.2", "10.0.0.1") + src_mac = dvs.runcmd("bash -c \"ip link show eth0 | grep ether | awk '{print $2}'\"")[1].strip().upper() + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet16"), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE", + "SAI_MIRROR_SESSION_ATTR_ERSPAN_ENCAPSULATION_TYPE": "SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL", + "SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_MIRROR_SESSION_ATTR_TOS": "32", + "SAI_MIRROR_SESSION_ATTR_TTL": "100", + "SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS": "1.1.1.1", + "SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS": "2.2.2.2", + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "02:04:06:08:10:12", + "SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS": src_mac, + "SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE": "25944"} + expected_state_db = {"status": "active", + "monitor_port": "Ethernet16", + "dst_mac": "02:04:06:08:10:12", + "route_prefix": "2.2.2.2/32"} + self.dvs_mirror.verify_session_status(session) + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, state_db=expected_state_db, src_ports=src_asic_ports, asic_size=11) + + # remove route + dvs.remove_route("2.2.2.2") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove neighbor + dvs.remove_neighbor("Ethernet16", "10.0.0.1") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove IP address + dvs.remove_ip_address("Ethernet16", "10.0.0.0/31") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # bring down Ethernet16 + dvs.set_interface_status("Ethernet16", "down") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove mirror session + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + def test_PortMirrorToVlanAddRemove(self, dvs, testlog): + """ + This test covers basic mirror session creation and removal operation + with destination port sits in a VLAN + Opeartion flow: + 1. Create mirror session with source ports. + 2. Create VLAN; assign IP; create neighbor; create FDB + The session should be up only at this time. + verify source port mirror config. + 3. Remove FDB; remove neighbor; remove IP; remove VLAN + 4. Remove mirror session + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + src_ports = "Ethernet12,Ethernet16" + src_asic_ports = ["Ethernet12", "Ethernet16"] + vlan_id = "10" + vlan = "Vlan10" + + # create mirror session + self.dvs_mirror.create_erspan_session(session, "5.5.5.5", "6.6.6.6", "0x6558", "8", "100", "0", None, src_ports, direction="TX") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create vlan; create vlan member + self.dvs_vlan.create_vlan(vlan_id) + self.dvs_vlan.create_vlan_member(vlan_id, "Ethernet4") + + # bring up vlan and member + dvs.set_interface_status(vlan, "up") + dvs.set_interface_status("Ethernet4", "up") + + # add ip address to vlan 6 + dvs.add_ip_address(vlan, "6.6.6.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create neighbor to vlan 6 + dvs.add_neighbor(vlan, "6.6.6.6", "66:66:66:66:66:66") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create fdb entry to ethernet4 + dvs.create_fdb(vlan_id, "66-66-66-66-66-66", "Ethernet4") + self.dvs_mirror.verify_session_status(session) + + src_mac = dvs.runcmd("bash -c \"ip link show eth0 | grep ether | awk '{print $2}'\"")[1].strip().upper() + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet4"), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_ENHANCED_REMOTE", + "SAI_MIRROR_SESSION_ATTR_ERSPAN_ENCAPSULATION_TYPE": "SAI_ERSPAN_ENCAPSULATION_TYPE_MIRROR_L3_GRE_TUNNEL", + "SAI_MIRROR_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_MIRROR_SESSION_ATTR_TOS": "32", + "SAI_MIRROR_SESSION_ATTR_TTL": "100", + "SAI_MIRROR_SESSION_ATTR_SRC_IP_ADDRESS": "5.5.5.5", + "SAI_MIRROR_SESSION_ATTR_DST_IP_ADDRESS": "6.6.6.6", + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "66:66:66:66:66:66", + "SAI_MIRROR_SESSION_ATTR_SRC_MAC_ADDRESS": src_mac, + "SAI_MIRROR_SESSION_ATTR_GRE_PROTOCOL_TYPE": "25944", + "SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID": "true", + "SAI_MIRROR_SESSION_ATTR_VLAN_TPID": "33024", + "SAI_MIRROR_SESSION_ATTR_VLAN_ID": vlan_id, + "SAI_MIRROR_SESSION_ATTR_VLAN_PRI": "0", + "SAI_MIRROR_SESSION_ATTR_VLAN_CFI": "0"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, asic_size=16, direction="TX") + + dvs.set_interface_status("Ethernet4", "down") + + # remove fdb entry + dvs.remove_fdb(vlan_id, "66-66-66-66-66-66") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove neighbor + dvs.remove_neighbor(vlan, "6.6.6.6") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove ip address + dvs.remove_ip_address(vlan, "6.6.6.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # bring down vlan and member + dvs.set_interface_status("Ethernet4", "down") + dvs.set_interface_status(vlan, "down") + + # remove vlan member; remove vlan + self.dvs_vlan.remove_vlan_member(vlan_id, "Ethernet4") + self.dvs_vlan.get_and_verify_vlan_member_ids(0) + self.dvs_vlan.remove_vlan(vlan_id) + + # remove mirror session + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + def test_PortMirrorToLagAddRemove(self, dvs, testlog): + """ + This test covers basic mirror session creation and removal operations + with destination port sits in a LAG + Operation flow: + 1. Create mirror sesion with source ports, direction + 2. Create LAG; assign IP; create directly connected neighbor + The session shoudl be up only at this time. + 3. Remove neighbor; remove IP; remove LAG + 4. Remove mirror session + + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + src_ports = "Ethernet0,Ethernet4" + src_asic_ports = ["Ethernet0", "Ethernet4"] + + # create mirror session + self.dvs_mirror.create_erspan_session(session, "10.10.10.10", "11.11.11.11", "0x6558", + "8", "100", "0", None, src_ports, "TX") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create port channel; create port channel member + self.dvs_lag.create_port_channel("008") + lag_entries = self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 1) + self.dvs_lag.create_port_channel_member("008", "Ethernet88") + + # Verify the LAG has been initialized properly + lag_member_entries = self.dvs_lag.get_and_verify_port_channel_members(1) + fvs = self.dvs_vlan.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER", lag_member_entries[0]) + assert len(fvs) == 4 + assert fvs.get("SAI_LAG_MEMBER_ATTR_LAG_ID") == lag_entries[0] + assert self.dvs_vlan.asic_db.port_to_id_map[fvs.get("SAI_LAG_MEMBER_ATTR_PORT_ID")] == "Ethernet88" + + # bring up port channel and port channel member + dvs.set_interface_status("PortChannel008", "up") + dvs.set_interface_status("Ethernet88", "up") + + # add ip address to port channel 008 + dvs.add_ip_address("PortChannel008", "11.11.11.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create neighbor to port channel 008 + dvs.add_neighbor("PortChannel008", "11.11.11.11", "88:88:88:88:88:88") + self.dvs_mirror.verify_session_status(session) + + # Check src_port state. + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet88"), + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "88:88:88:88:88:88"} + + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, direction="TX") + + # remove neighbor + dvs.remove_neighbor("PortChannel008", "11.11.11.11") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove ip address + dvs.remove_ip_address("PortChannel008", "11.11.11.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # bring down port channel and port channel member + dvs.set_interface_status("PortChannel008", "down") + dvs.set_interface_status("Ethernet88", "down") + + # remove port channel member; remove port channel + + self.dvs_lag.remove_port_channel_member("008", "Ethernet88") + self.dvs_lag.remove_port_channel("008") + + # remove mirror session + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + def test_PortMirrorDestMoveVlan(self, dvs, testlog): + """ + This test tests mirror session destination move from non-VLAN to VLAN + and back to non-VLAN port + 1. Create mirror session + 2. Enable non-VLAN monitor port + 3. Create VLAN; move to VLAN without FDB entry + 4. Create FDB entry + 5. Remove FDB entry + 6. Remove VLAN; move to non-VLAN + 7. Disable non-VLAN monitor port + 8. Remove mirror session + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + src_ports = "Ethernet0" + src_asic_ports = ["Ethernet0"] + + # create mirror session + self.dvs_mirror.create_erspan_session(session, "7.7.7.7", "8.8.8.8", "0x6558", "8", "100", "0", None, src_ports) + self.dvs_mirror.verify_session_status(session, status="inactive") + + # bring up port; add ip; add neighbor; add route + dvs.set_interface_status("Ethernet32", "up") + dvs.add_ip_address("Ethernet32", "80.0.0.0/31") + dvs.add_neighbor("Ethernet32", "80.0.0.1", "02:04:06:08:10:12") + dvs.add_route("8.8.0.0/16", "80.0.0.1") + + self.dvs_mirror.verify_session_status(session) + + # check monitor port + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet32")} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db) + + # mirror session move round 1 + # create vlan; create vlan member; bring up vlan and member + self.dvs_vlan.create_vlan("9") + self.dvs_vlan.create_vlan_member("9", "Ethernet48") + dvs.set_interface_status("Vlan9", "up") + dvs.set_interface_status("Ethernet48", "up") + + self.dvs_mirror.verify_session_status(session) + + # add ip address to vlan 9 + dvs.add_ip_address("Vlan9", "8.8.8.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create neighbor to vlan 9 + dvs.add_neighbor("Vlan9", "8.8.8.8", "88:88:88:88:88:88") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create fdb entry to ethernet48 + dvs.create_fdb("9", "88-88-88-88-88-88", "Ethernet48") + self.dvs_mirror.verify_session_status(session) + + # check monitor port + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet48"), + "SAI_MIRROR_SESSION_ATTR_VLAN_HEADER_VALID": "true", + "SAI_MIRROR_SESSION_ATTR_VLAN_TPID": "33024", + "SAI_MIRROR_SESSION_ATTR_VLAN_ID": "9", + "SAI_MIRROR_SESSION_ATTR_VLAN_PRI": "0", + "SAI_MIRROR_SESSION_ATTR_VLAN_CFI": "0"} + + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports) + + # mirror session move round 2 + # remove fdb entry + dvs.remove_fdb("9", "88-88-88-88-88-88") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove neighbor + dvs.remove_neighbor("Vlan9", "8.8.8.8") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove ip address + dvs.remove_ip_address("Vlan9", "8.8.8.0/24") + self.dvs_mirror.verify_session_status(session) + + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet32")} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db) + + # bring down vlan and member; remove vlan member; remove vlan + dvs.set_interface_status("Ethernet48", "down") + dvs.set_interface_status("Vlan9", "down") + self.dvs_vlan.remove_vlan_member("9", "Ethernet48") + self.dvs_vlan.get_and_verify_vlan_member_ids(0) + self.dvs_vlan.remove_vlan("9") + + # remove route; remove neighbor; remove ip; bring down port + dvs.remove_route("8.8.8.0/24") + dvs.remove_neighbor("Ethernet32", "80.0.0.1") + dvs.remove_ip_address("Ethernet32", "80.0.0.0/31") + dvs.set_interface_status("Ethernet32", "down") + + # remove mirror session + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + + def test_PortMirrorDestMoveLag(self, dvs, testlog): + """ + This test tests mirror session destination move from non-LAG to LAG + and back to non-LAG port + 1. Create mirror session with source ports. + 2. Enable non-LAG monitor port + 3. Create LAG; move to LAG with one member + 4. Remove LAG member + 5. Create LAG member + 6. Remove LAG; move to non-LAG + 7. Disable non-LAG monitor port + 8. Remove mirror session + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + src_ports = "Ethernet0,Ethernet4" + src_asic_ports = ["Ethernet0", "Ethernet4"] + # create mirror session + self.dvs_mirror.create_erspan_session(session, "12.12.12.12", "13.13.13.13", "0x6558", "8", "100", "0", None, src_ports, direction="RX") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # bring up port; add ip; add neighbor; add route + dvs.set_interface_status("Ethernet64", "up") + dvs.add_ip_address("Ethernet64", "100.0.0.0/31") + dvs.add_neighbor("Ethernet64", "100.0.0.1", "02:04:06:08:10:12") + dvs.add_route("13.13.0.0/16", "100.0.0.1") + self.dvs_mirror.verify_session_status(session) + + # check monitor port + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet64"), + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "02:04:06:08:10:12"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, direction="RX") + + # mirror session move round 1 + # create port channel; create port channel member; bring up + self.dvs_lag.create_port_channel("080") + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 1) + self.dvs_lag.create_port_channel_member("080", "Ethernet32") + dvs.set_interface_status("PortChannel080", "up") + dvs.set_interface_status("Ethernet32", "up") + + # add ip address to port channel 080; create neighbor to port channel 080 + dvs.add_ip_address("PortChannel080", "200.0.0.0/31") + dvs.add_neighbor("PortChannel080", "200.0.0.1", "12:10:08:06:04:02") + self.dvs_mirror.verify_session_status(session) + + # add route + dvs.add_route("13.13.13.0/24", "200.0.0.1") + self.dvs_mirror.verify_session_status(session) + + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet32"), + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "12:10:08:06:04:02"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, direction="RX") + + # mirror session move round 2 + # remove port channel member + self.dvs_lag.remove_port_channel_member("080", "Ethernet32") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # mirror session move round 3 + # create port channel member + self.dvs_lag.create_port_channel_member("080", "Ethernet32") + self.dvs_mirror.verify_session_status(session) + + # check monitor port + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet32"), + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "12:10:08:06:04:02"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, direction="RX") + + # mirror session move round 4 + # remove route + dvs.remove_route("13.13.13.0/24") + self.dvs_mirror.verify_session_status(session) + + # check monitor port + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet64"), + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "02:04:06:08:10:12"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, direction="RX") + + # remove neighbor; remove ip address to port channel 080 + dvs.remove_neighbor("PortChannel080", "200.0.0.1") + dvs.remove_ip_address("PortChannel080", "200.0.0.0/31") + + # bring down; remove port channel member; remove port channel + dvs.set_interface_status("Ethernet32", "down") + dvs.set_interface_status("PortChannel080", "down") + self.dvs_lag.remove_port_channel_member("080", "Ethernet32") + self.dvs_lag.remove_port_channel("080") + self.dvs_mirror.verify_session_status(session) + + + # remove route; remove neighbor; remove ip; bring down port + dvs.remove_route("13.13.0.0/16") + dvs.remove_neighbor("Ethernet64", "100.0.0.1") + dvs.remove_ip_address("Ethernet64", "100.0.0.0/31") + dvs.set_interface_status("Ethernet64", "down") + self.dvs_mirror.verify_session_status(session, status="inactive") + + + # remove mirror session + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + def test_LAGMirrorToERSPANLagAddRemove(self, dvs, testlog): + """ + This test covers basic LAG mirror session creation and removal operations + with destination port sits in a LAG + Operation flow: + 1. Create source LAG + 2. configure mirror sesion with LAG as source port. + 3. Create destination LAG; assign IP; create directly connected neighbor + The session shoudl be up only at this time. + 4. Remove neighbor; remove IP; remove LAG + 5. Remove mirror session + + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + src_port1="Ethernet0" + po_src_port="PortChannel001" + src_port2="Ethernet4" + src_ports = "PortChannel001,Ethernet8" + src_asic_ports = ["Ethernet0", "Ethernet4", "Ethernet8"] + + # create port channel; create port channel member + self.dvs_lag.create_port_channel("001") + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 1) + self.dvs_lag.create_port_channel_member("001", src_port1) + self.dvs_lag.create_port_channel_member("001", src_port2) + + # bring up port channel and port channel member + dvs.set_interface_status(po_src_port, "up") + dvs.set_interface_status(src_port1, "up") + dvs.set_interface_status(src_port2, "up") + + # create mirror session + self.dvs_mirror.create_erspan_session(session, "10.10.10.10", "11.11.11.11", "0x6558", "8", "100", "0", None, src_ports) + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create port channel; create port channel member + self.dvs_lag.create_port_channel("008") + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 2) + self.dvs_lag.create_port_channel_member("008", "Ethernet88") + + # bring up port channel and port channel member + dvs.set_interface_status("PortChannel008", "up") + dvs.set_interface_status("Ethernet88", "up") + + # add ip address to port channel 008 + dvs.add_ip_address("PortChannel008", "11.11.11.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # create neighbor to port channel 008 + dvs.add_neighbor("PortChannel008", "11.11.11.11", "88:88:88:88:88:88") + self.dvs_mirror.verify_session_status(session) + + # Check src_port state. + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet88"), + "SAI_MIRROR_SESSION_ATTR_DST_MAC_ADDRESS": "88:88:88:88:88:88"} + + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports) + + # remove neighbor + dvs.remove_neighbor("PortChannel008", "11.11.11.11") + self.dvs_mirror.verify_session_status(session, status="inactive") + + # remove ip address + dvs.remove_ip_address("PortChannel008", "11.11.11.0/24") + self.dvs_mirror.verify_session_status(session, status="inactive") + # bring down port channel and port channel member + dvs.set_interface_status("PortChannel008", "down") + dvs.set_interface_status("Ethernet88", "down") + + # remove port channel member; remove port channel + self.dvs_lag.remove_port_channel_member("008", "Ethernet88") + self.dvs_lag.remove_port_channel("008") + self.dvs_lag.remove_port_channel_member("001", src_port1) + self.dvs_lag.remove_port_channel_member("001", src_port2) + self.dvs_lag.remove_port_channel("001") + + # remove mirror session + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + diff --git a/tests/test_mirror_port_span.py b/tests/test_mirror_port_span.py new file mode 100644 index 000000000000..e5f8c683ec05 --- /dev/null +++ b/tests/test_mirror_port_span.py @@ -0,0 +1,470 @@ +# This test suite covers the functionality of mirror feature in SwSS +import pytest + +@pytest.mark.usefixtures("testlog") +@pytest.mark.usefixtures('dvs_vlan_manager') +@pytest.mark.usefixtures('dvs_lag_manager') +@pytest.mark.usefixtures('dvs_mirror_manager') +@pytest.mark.usefixtures('dvs_acl_manager') +@pytest.mark.usefixtures('dvs_policer_manager') + +class TestMirror(object): + def test_PortMirrorAddRemove(self, dvs, testlog): + """ + This test covers the basic SPAN mirror session creation and removal operations + Operation flow: + 1. Create mirror session with only dst_port , verify session becomes active. + 2. Create mirror session with invalid dst_port, verify session doesnt get created. + 3. Create mirror session with invalid source port, verify session doesnt get created. + 4. Create mirror session with source port, verify session becomes active + 5. Create mirror session with Vlan as dst_port, verify session doesnt get created. + 6. Create mirror session with Vlan as source port, verify session doesnt get created. + """ + + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + dst_port = "Ethernet16" + + # Sub Test 1 + self.dvs_mirror.create_span_session(session, dst_port) + self.dvs_mirror.verify_session_status(session) + a = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet16"), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session(dvs, session, asic_db=a) + self.dvs_mirror.remove_mirror_session(session) + + # Sub Test 2 + self.dvs_mirror.create_span_session(session, "Invalid") + self.dvs_mirror.verify_session_status(session, expected=0) + + # Sub Test 3 + self.dvs_mirror.create_span_session(session, dst_port, "Invalid", "RX") + self.dvs_mirror.verify_session_status(session, expected=0) + + # Sub Test 4 + # create mirror session with dst_port, src_port, direction + src_ports = "Ethernet12" + src_asic_ports = ["Ethernet12"] + self.dvs_mirror.create_span_session(session, dst_port, src_ports, "RX") + self.dvs_mirror.verify_session_status(session) + self.dvs_mirror.verify_session(dvs, session, asic_db=a, src_ports=src_asic_ports, direction = "RX") + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + ## Sub Test 5 + self.dvs_vlan.create_vlan("10") + self.dvs_mirror.create_span_session(session, dst_port="Vlan10") + self.dvs_mirror.verify_session_status(session, expected=0) + + ## Sub Test 6 + self.dvs_mirror.create_span_session(session, dst_port, src_ports="Vlan10") + self.dvs_mirror.verify_session_status(session, expected=0) + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + self.dvs_vlan.remove_vlan("10") + self.dvs_vlan.get_and_verify_vlan_ids(0) + + + def test_PortMirrorMultiSpanAddRemove(self, dvs, testlog): + """ + This test covers the Multiple SPAN mirror session creation and removal operations + Operation flow: + 1. Create mirror session with multiple source ports, verify that session is active + 2. Create mirror session with multiple source with valid,invalid ports, session doesnt get created. + 3. Create mirror session with multiple source with invalid destination, session doesnt get created. + 4. Create two mirror sessions with multiple source ports. + 5. Verify session config in both sessions. + """ + + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session1 = "TEST_SESSION1" + session2 = "TEST_SESSION2" + dst_port1 = "Ethernet16" + dst_oid1 = pmap.get(dst_port1) + dst_port2 = "Ethernet20" + dst_oid2 = pmap.get(dst_port2) + + # Sub test 1 + src_ports = "Ethernet0,Ethernet4,Ethernet8" + src_asic_ports = ["Ethernet0","Ethernet4","Ethernet8"] + self.dvs_mirror.create_span_session(session1, dst_port1, src_ports) + a = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": dst_oid1, + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session_status(session1) + self.dvs_mirror.verify_session(dvs, session1, asic_db=a, src_ports=src_asic_ports) + self.dvs_mirror.remove_mirror_session(session1) + self.dvs_mirror.verify_no_mirror() + + #Subtest 2 + + # create mirror session with valid and invalid ports. + src_ports = "Ethernet0,Invalid,Ethernet8" + self.dvs_mirror.create_span_session(session1, dst_port1, src_ports) + self.dvs_mirror.verify_session_status(session1, expected=0) + + # Subtest 3 + src_ports = "Ethernet0,Ethernet4,Ethernet8" + self.dvs_mirror.create_span_session(session1, "Invalid", src_ports) + self.dvs_mirror.verify_session_status(session1, expected=0) + + # create mirror session + src_ports1 = "Ethernet0,Ethernet4" + src_asic_ports1 = ["Ethernet0","Ethernet4"] + self.dvs_mirror.create_span_session(session1, dst_port1, src_ports1) + src_ports2 = "Ethernet8,Ethernet12" + src_asic_ports2 = ["Ethernet8","Ethernet12"] + self.dvs_mirror.create_span_session(session2, dst_port2, src_ports2) + + a1 = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": dst_oid1, + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + a2 = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": dst_oid2, + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session_status(session1, expected = 2) + self.dvs_mirror.verify_session_status(session2, expected = 2) + self.dvs_mirror.verify_session(dvs, session1, dst_oid=dst_oid1, asic_db=a1, src_ports=src_asic_ports1, expected = 2) + self.dvs_mirror.verify_session(dvs, session2, dst_oid=dst_oid2, asic_db=a2, src_ports=src_asic_ports2, expected = 2) + self.dvs_mirror.remove_mirror_session(session1) + self.dvs_mirror.remove_mirror_session(session2) + self.dvs_mirror.verify_no_mirror() + + def test_PortMirrorPolicerAddRemove(self, dvs, testlog): + """ + This test covers the basic SPAN mirror session creation and removal operations + Operation flow: + 1. Create mirror session with only dst_port and policer , verify session becomes active + 2. Create session with invalid policer, verify session doesnt get created. + 2. Create mirror with policer and multiple source ports, verify session config on all ports. + """ + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + session = "TEST_SESSION" + dst_port = "Ethernet16" + policer="POLICER" + + #Sub Test 1 + self.dvs_policer.create_policer(policer) + self.dvs_policer.verify_policer(policer) + self.dvs_mirror.create_span_session(session, dst_port, policer="POLICER") + self.dvs_mirror.verify_session_status(session) + a = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get("Ethernet16"), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session(dvs, session, asic_db=a, policer="POLICER") + self.dvs_mirror.remove_mirror_session(session) + self.dvs_policer.remove_policer("POLICER") + self.dvs_policer.verify_no_policer() + self.dvs_mirror.verify_no_mirror() + + # Sub test 2 + src_ports = "Ethernet0,Ethernet4,Ethernet8" + src_asic_ports = ["Ethernet0","Ethernet4","Ethernet8"] + self.dvs_mirror.create_span_session(session, dst_port, src_ports, policer="POLICER") + self.dvs_mirror.verify_session_status(session, expected=0) + + # Sub test 3 + self.dvs_policer.create_policer(policer) + self.dvs_policer.verify_policer(policer) + self.dvs_mirror.create_span_session(session, dst_port, src_ports, policer="POLICER") + self.dvs_mirror.verify_session_status(session) + self.dvs_mirror.verify_session(dvs, session, asic_db=a, src_ports=src_asic_ports, policer="POLICER") + self.dvs_mirror.remove_mirror_session(session) + self.dvs_policer.remove_policer("POLICER") + self.dvs_policer.verify_no_policer() + self.dvs_mirror.verify_no_mirror() + + + def test_PortMultiMirrorPolicerAddRemove(self, dvs, testlog): + """ + This test covers the basic SPAN mirror session creation and removal operations + Operation flow: + 1. Create mirror session with multiple source with multiple policer. + 2. Verify port/policer/session config on all. + """ + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session1 = "TEST_SESSION1" + session2 = "TEST_SESSION2" + dst_port1 = "Ethernet16" + dst_oid1 = pmap.get(dst_port1) + dst_port2 = "Ethernet20" + dst_oid2 = pmap.get(dst_port2) + policer1 = "POLICER1" + policer2 = "POLICER2" + + #Sub Test 1 + self.dvs_policer.create_policer(policer1, cir="600") + self.dvs_policer.verify_policer(policer1) + self.dvs_policer.create_policer(policer2, cir="800") + self.dvs_policer.verify_policer(policer2, expected = 2) + + src_ports1 = "Ethernet0,Ethernet4" + src_asic_ports1 = ["Ethernet0","Ethernet4"] + self.dvs_mirror.create_span_session(session1, dst_port1, src_ports1, policer=policer1) + src_ports2 = "Ethernet8,Ethernet12" + src_asic_ports2 = ["Ethernet8","Ethernet12"] + self.dvs_mirror.create_span_session(session2, dst_port2, src_ports2, policer=policer2) + + a1 = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": dst_oid1, + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + a2 = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": dst_oid2, + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session_status(session1, expected = 2) + self.dvs_mirror.verify_session_status(session2, expected = 2) + self.dvs_mirror.verify_session(dvs, session1, dst_oid=dst_oid1, asic_db=a1, src_ports=src_asic_ports1, expected = 2, policer=policer1) + self.dvs_mirror.verify_session(dvs, session2, dst_oid=dst_oid2, asic_db=a2, src_ports=src_asic_ports2, expected = 2, policer=policer2) + self.dvs_mirror.remove_mirror_session(session1) + self.dvs_mirror.remove_mirror_session(session2) + self.dvs_policer.remove_policer(policer1) + self.dvs_policer.remove_policer(policer2) + self.dvs_policer.verify_no_policer() + self.dvs_mirror.verify_no_mirror() + + def test_LAGMirrorSpanAddRemove(self, dvs, testlog): + """ + This test covers the LAG mirror session creation and removal operations + Operation flow: + 1. Create port channel with 2 members. + 2. Create mirror session with LAG as source port. + 3. Verify that source ports have proper mirror config. + 4. Remove port from port-channel and verify mirror config is removed from the port. + 5. Remove second port and verify mirror config is removed. + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + dst_port = "Ethernet16" + src_port1="Ethernet8" + src_port2="Ethernet4" + po_src_port="PortChannel008" + src_ports = "PortChannel008,Ethernet12" + src_asic_ports = ["Ethernet8", "Ethernet4", "Ethernet12"] + + # create port channel; create port channel member + self.dvs_lag.create_port_channel("008") + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 1) + self.dvs_lag.create_port_channel_member("008", src_port1) + self.dvs_lag.create_port_channel_member("008", src_port2) + + # bring up port channel and port channel member + dvs.set_interface_status(po_src_port, "up") + dvs.set_interface_status(src_port1, "up") + dvs.set_interface_status(src_port2, "up") + + # Sub Test 1 + self.dvs_mirror.create_span_session(session, dst_port, src_ports) + self.dvs_mirror.verify_session_status(session) + + # verify asicdb + # Check src_port state. + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get(dst_port), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, asic_size=2) + + # Sub Test 2 + # remove port channel member; remove port channel + self.dvs_lag.remove_port_channel_member("008", src_port1) + src_asic_ports = ["Ethernet4", "Ethernet12"] + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, asic_size=2) + self.dvs_lag.remove_port_channel_member("008", src_port2) + + self.dvs_lag.remove_port_channel("008") + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + def test_PortMirrorPolicerWithAcl(self, dvs, testlog): + """ + This test covers the port mirroring with policer and ACL configurations. + Operation flow: + 1. Create port mirror session with policer. + 2. Create ACL and configure mirror + 2. Verify mirror and ACL config is proper. + """ + dvs.setup_db() + session = "MIRROR_SESSION" + policer= "POLICER" + dst_port = "Ethernet16" + + # create policer + self.dvs_policer.create_policer(policer) + self.dvs_policer.verify_policer(policer) + + # create mirror session + self.dvs_mirror.create_span_session(session, dst_port, policer=policer) + self.dvs_mirror.verify_session_status(session) + + member_ids = dvs.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION", 1) + + # create acl table + bind_ports = ["Ethernet0", "Ethernet4"] + self.dvs_acl.create_acl_table("test", "mirror", bind_ports) + self.dvs_acl.verify_acl_group_num(len(bind_ports)) + + config_qualifiers = {"mirror_action": session, + "DSCP": "8/56"} + mirror_oid="1:" + member_ids[0] + expected_sai_qualifiers = {"SAI_ACL_ENTRY_ATTR_FIELD_DSCP": self.dvs_acl.get_simple_qualifier_comparator("8&mask:0x38"), + "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": self.dvs_acl.get_simple_qualifier_comparator(mirror_oid)} + self.dvs_acl.create_mirror_acl_rule("test", "mirror_rule", config_qualifiers) + self.dvs_acl.verify_acl_rule(expected_sai_qualifiers) + self.dvs_acl.remove_acl_rule("test", "mirror_rule") + self.dvs_acl.verify_no_acl_rules() + + self.dvs_acl.remove_acl_table("test") + self.dvs_acl.verify_acl_table_count(0) + + self.dvs_mirror.remove_mirror_session(session) + + self.dvs_policer.remove_policer(policer) + self.dvs_policer.verify_no_policer() + self.dvs_mirror.verify_no_mirror() + + def test_PortMirrorLAGPortSpanAddRemove(self, dvs, testlog): + """ + This test covers the LAG mirror session creation and removal operations + Operation flow: + 1. Create port channel with 2 members. + 2. Create mirror session with LAG and LAG port. session creation has to fail. + 3. Create mirror session with LAG and other LAG port. session creation has to fail + 4. Create mirror session with LAG and new port, session will become active. + 5. Verify all LAG and new port has session config. + 6. Remove one LAG member and verify that failing session works fine. + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + dst_port = "Ethernet16" + src_port1="Ethernet8" + src_port2="Ethernet4" + po_src_port="PortChannel008" + + # create port channel; create port channel member + self.dvs_lag.create_port_channel("008") + self.dvs_lag.get_and_verify_port_channel(1) + self.dvs_lag.create_port_channel_member("008", src_port1) + self.dvs_lag.create_port_channel_member("008", src_port2) + self.dvs_lag.get_and_verify_port_channel_members(2) + + # bring up port channel and port channel member + dvs.set_interface_status(po_src_port, "up") + dvs.set_interface_status(src_port1, "up") + dvs.set_interface_status(src_port2, "up") + + # Sub Test 1 + src_ports = "PortChannel008,Ethernet8" + self.dvs_mirror.create_span_session(session, dst_port, src_ports) + self.dvs_mirror.verify_session_status(session, expected=0) + self.dvs_mirror.verify_session_status(session, status="inactive", expected = 0) + + # Sub Test 2 + src_ports = "PortChannel008,Ethernet4" + self.dvs_mirror.create_span_session(session, dst_port, src_ports) + self.dvs_mirror.verify_session_status(session, expected=0) + self.dvs_mirror.verify_session_status(session, status="inactive", expected=0) + + # Sub Test 3 + src_ports = "PortChannel008,Ethernet40" + src_asic_ports = ["Ethernet8", "Ethernet40", "Ethernet4"] + self.dvs_mirror.create_span_session(session, dst_port, src_ports) + self.dvs_mirror.verify_session_status(session, status="active") + + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get(dst_port), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, asic_size=2) + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + + # Sub Test 4 + self.dvs_lag.remove_port_channel_member("008", "Ethernet4") + self.dvs_lag.get_and_verify_port_channel_members(1) + src_ports = "PortChannel008,Ethernet40" + src_asic_ports = ["Ethernet8", "Ethernet40"] + self.dvs_mirror.create_span_session(session, dst_port, src_ports) + self.dvs_mirror.verify_session_status(session) + self.dvs_mirror.verify_session(dvs, session, src_ports=src_asic_ports) + + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + self.dvs_lag.remove_port_channel_member("008", "Ethernet8") + self.dvs_lag.get_and_verify_port_channel_members(0) + self.dvs_lag.remove_port_channel("008") + self.dvs_lag.get_and_verify_port_channel(0) + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 0) + + + def test_PortLAGMirrorUpdateLAG(self, dvs, testlog): + """ + This test covers the LAG mirror session creation and removal operations + Operation flow: + 1. Create port channel with 2 members. + 2. Create mirror session with LAG and other port P1 + 3. Verify mirror session is active and ports have proper config + 4. Add port P1 to LAG and verify mirror config on all ports. + 5. Remove port P1 from LAG and verify mirror config on P1 is intact. + 6. Remove port from LAG and verify mirror config on other ports in intact. + """ + dvs.setup_db() + pmap = dvs.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "") + pmap = dict(pmap) + + session = "TEST_SESSION" + dst_port = "Ethernet16" + src_port1="Ethernet8" + src_port2="Ethernet4" + po_src_port="PortChannel008" + + # create port channel; create port channel member + self.dvs_lag.create_port_channel("008") + self.dvs_lag.get_and_verify_port_channel(1) + self.dvs_lag.create_port_channel_member("008", src_port1) + self.dvs_lag.create_port_channel_member("008", src_port2) + self.dvs_lag.get_and_verify_port_channel_members(2) + + # bring up port channel and port channel member + dvs.set_interface_status(po_src_port, "up") + dvs.set_interface_status(src_port1, "up") + dvs.set_interface_status(src_port2, "up") + + # Sub Test 1 + src_ports = "PortChannel008,Ethernet40" + src_asic_ports = ["Ethernet8", "Ethernet40", "Ethernet4"] + self.dvs_mirror.create_span_session(session, dst_port, src_ports) + self.dvs_mirror.verify_session_status(session, status="active") + + expected_asic_db = {"SAI_MIRROR_SESSION_ATTR_MONITOR_PORT": pmap.get(dst_port), + "SAI_MIRROR_SESSION_ATTR_TYPE": "SAI_MIRROR_SESSION_TYPE_LOCAL"} + self.dvs_mirror.verify_session(dvs, session, asic_db=expected_asic_db, src_ports=src_asic_ports, asic_size=2) + + + # Add source port Ethernet40 to LAG + self.dvs_lag.create_port_channel_member("008", "Ethernet40") + self.dvs_lag.get_and_verify_port_channel_members(3) + self.dvs_mirror.verify_session(dvs, session, src_ports=src_asic_ports) + + # Remove source port Ethernet40 from LAG + self.dvs_lag.remove_port_channel_member("008", "Ethernet40") + self.dvs_lag.get_and_verify_port_channel_members(2) + self.dvs_mirror.verify_session(dvs, session, src_ports=src_asic_ports) + + # Remove one port from LAG + self.dvs_lag.remove_port_channel_member("008", "Ethernet4") + self.dvs_lag.get_and_verify_port_channel_members(1) + src_asic_ports = ["Ethernet8"] + self.dvs_mirror.verify_session(dvs, session, src_ports=src_asic_ports) + + #cleanup + self.dvs_mirror.remove_mirror_session(session) + self.dvs_mirror.verify_no_mirror() + self.dvs_lag.remove_port_channel_member("008", "Ethernet8") + self.dvs_lag.get_and_verify_port_channel_members(0) + self.dvs_lag.remove_port_channel("008") + self.dvs_lag.get_and_verify_port_channel(0) + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 0) +