Repository: sonic-net/sonic-mgmt

Issue:
Need new test: tagged packets hits vlan port, query SNMP FDB
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.

If you suspect your issue is a bug, please edit your issue description to
include the BUG REPORT INFORMATION shown below. If you fail to provide this
information within 7 days, we cannot debug your issue and will close it. We
will, however, reopen it if you later provide the information.

For more information about reporting issues, see
https://github.com/Azure/SONiC/wiki#report-issues

---------------------------------------------------
GENERAL SUPPORT INFORMATION
---------------------------------------------------

The GitHub issue tracker is for bug reports and feature requests.
General support can be found at the following locations:

- SONiC Support Forums - https://groups.google.com/forum/#!forum/sonicproject

---------------------------------------------------
BUG REPORT INFORMATION
---------------------------------------------------
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->

**Description**
As the title

**Steps to reproduce the issue:**
The port or port channel members in a vlan could be tagged or untagged. The existing testcase is for untagged. With the multiple vlans overlapping, there are tagged port/portchannel. We need new testcases for such configuration.

**Describe the results you received:**


**Describe the results you expected:**


**Additional information you deem important:**
<!--
software/ASIC/Hardware Flatform version and info
-->
    **Output of `show version`:**

    ```
    (paste your output here)
    ```

    **Attach debug file `sudo generate_dump`:**

    ```
    (paste your output here)
    ```



Discussion:

Resolving patch:
diff --git a/ansible/library/snmp_facts.py b/ansible/library/snmp_facts.py
--- a/ansible/library/snmp_facts.py
+++ b/ansible/library/snmp_facts.py
@@ -226,6 +226,9 @@ def __init__(self,dotprefix=False):
         self.ipCidrRouteEntry = dp + "1.3.6.1.2.1.4.24.4.1.1.0.0.0.0.0.0.0.0.0" # + .next hop IP
         self.ipCidrRouteStatus = dp + "1.3.6.1.2.1.4.24.4.1.16.0.0.0.0.0.0.0.0.0" # + .next hop IP
 
+        # Dot1q MIB
+        self.dot1qTpFdbEntry = dp + "1.3.6.1.2.1.17.7.1.2.2.1.2" # + .VLAN.MAC
+
 def decode_hex(hexstring):
 
     if len(hexstring) < 3:
@@ -923,7 +926,6 @@ def main():
 
         for oid, val in varBinds:
             current_oid = oid.prettyPrint()
-            current_val = val.prettyPrint()
             if current_oid == v.sysTotalMemery:
                 results['ansible_sysTotalMemery'] = decode_type(module, current_oid, val)
             elif current_oid == v.sysTotalFreeMemery:
@@ -935,6 +937,31 @@ def main():
             elif current_oid == v.sysCachedMemory:
                 results['ansible_sysCachedMemory'] = decode_type(module, current_oid, val)
 
+        errorIndication, errorStatus, errorIndex, varTable = cmdGen.nextCmd(
+            snmp_auth,
+            cmdgen.UdpTransportTarget((m_args['host'], 161)),
+            cmdgen.MibVariable(p.dot1qTpFdbEntry,),
+        )
+
+        if errorIndication:
+            module.fail_json(msg=str(errorIndication) + ' querying FdbTable')
+
+        for varBinds in varTable:
+            for oid, val in varBinds:
+                current_oid = oid.prettyPrint()
+                current_val = val.prettyPrint()
+                if v.dot1qTpFdbEntry in current_oid:
+                    # extract fdb info from oid
+                    items = current_oid.split(v.dot1qTpFdbEntry + ".")[1].split(".")
+                    # VLAN + MAC(6)
+                    if len(items) != 7:
+                        continue
+                    mac_str = "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(
+                        int(items[1]), int(items[2]), int(items[3]), int(items[4]), int(items[5]), int(items[6]))
+                    # key must be string
+                    key = items[0] + '.' + mac_str
+                    results['snmp_fdb'][key] = current_val
+
     module.exit_json(ansible_facts=results)
 
 main()


Test patch:
diff --git a/tests/snmp/test_snmp_fdb.py b/tests/snmp/test_snmp_fdb.py
new file mode 100644
--- /dev/null
+++ b/tests/snmp/test_snmp_fdb.py
@@ -0,0 +1,106 @@
+import pytest
+import ptf.testutils as testutils
+import logging
+import pprint
+
+from tests.common.fixtures.ptfhost_utils import change_mac_addresses        # lgtm[py/unused-import]
+from tests.common.dualtor.mux_simulator_control import toggle_all_simulator_ports_to_rand_selected_tor  # lgtm[py/unused-import]
+from tests.common.fixtures.duthost_utils import ports_list, vlan_ports_list
+from tests.common.utilities import wait_until
+from tests.common.helpers.snmp_helpers import get_snmp_facts
+
+logger = logging.getLogger(__name__)
+
+pytestmark = [
+    pytest.mark.topology('t0')
+]
+
+# Use original ports intead of sub interfaces for ptfadapter if it's t0-backend
+PTF_PORT_MAPPING_MODE = "use_orig_interface"
+
+DUMMY_MAC_PREFIX = "02:11:22:33"
+
+def get_fdb_dynamic_mac_count(duthost):
+    res = duthost.command('show mac')
+    logger.info('"show mac" output on DUT:\n{}'.format(pprint.pformat(res['stdout_lines'])))
+    total_mac_count = 0
+    for l in res['stdout_lines']:
+        if "dynamic" in l.lower() and DUMMY_MAC_PREFIX in l.lower():
+            total_mac_count += 1
+    return total_mac_count
+
+
+def fdb_table_has_no_dynamic_macs(duthost):
+    return (get_fdb_dynamic_mac_count(duthost) == 0)
+
+
+@pytest.fixture(scope="module", autouse=True)
+def fdb_cleanup(duthost):
+    """ cleanup FDB before test run """
+    if fdb_table_has_no_dynamic_macs(duthost):
+        return
+    else:
+        duthost.command('sonic-clear fdb all')
+        assert wait_until(20, 2, 0, fdb_table_has_no_dynamic_macs, duthost), "FDB Table Cleanup failed"
+
+
+def build_icmp_packet(vlan_id, src_mac="00:22:00:00:00:02", dst_mac="ff:ff:ff:ff:ff:ff",
+                        src_ip="192.168.0.1", dst_ip="192.168.0.2", ttl=64):
+
+    pkt = testutils.simple_icmp_packet(pktlen=100 if vlan_id == 0 else 104,
+                                eth_dst=dst_mac,
+                                eth_src=src_mac,
+                                dl_vlan_enable=False if vlan_id == 0 else True,
+                                vlan_vid=vlan_id,
+                                vlan_pcp=0,
+                                ip_src=src_ip,
+                                ip_dst=dst_ip,
+                                ip_ttl=ttl)
+    return pkt
+
+
+@pytest.mark.bsl
+def test_snmp_fdb_send_tagged(ptfadapter, vlan_ports_list, toggle_all_simulator_ports_to_rand_selected_tor, duthost, localhost, creds_all_duts):
+    """
+    Send tagged packets from each port.
+    Verify SNMP FDB entry
+    """
+    cfg_facts = duthost.config_facts(host=duthost.hostname, source="persistent")['ansible_facts']
+    config_portchannels = cfg_facts.get('PORTCHANNEL', {})
+    send_cnt = 0
+    send_portchannels_cnt = 0
+    for vlan_port in vlan_ports_list:
+        port_index = vlan_port["port_index"][0]
+        for permit_vlanid in map(int, vlan_port["permit_vlanid"]):
+            dummy_mac = '{}:{:02x}:{:02x}'.format(DUMMY_MAC_PREFIX, (port_index>>8)&0xFF, port_index&0xFF)
+            pkt = build_icmp_packet(permit_vlanid, dummy_mac)
+            logger.info("Send tagged({}) packet from {} ...".format(permit_vlanid, port_index))
+            logger.info(pkt.sprintf("%Ether.src% %IP.src% -> %Ether.dst% %IP.dst%"))
+            testutils.send(ptfadapter, port_index, pkt)
+            send_cnt += 1
+            if vlan_port['dev'] in config_portchannels:
+                send_portchannels_cnt += 1
+    # Flush dataplane
+    ptfadapter.dataplane.flush()
+
+    hostip = duthost.host.options['inventory_manager'].get_host(duthost.hostname).vars['ansible_host']
+    snmp_facts = get_snmp_facts(localhost, host=hostip, version="v2c", community=creds_all_duts[duthost]["snmp_rocommunity"], wait=True)['ansible_facts']
+    assert 'snmp_fdb' in snmp_facts
+    assert 'snmp_interfaces' in snmp_facts
+    dummy_mac_cnt = 0
+    recv_portchannels_cnt = 0
+    for key in snmp_facts['snmp_fdb']:
+        # key is string: vlan.mac
+        items = key.split('.')
+        if len(items) != 2:
+            continue
+        logger.info("FDB entry: {}".format(items))
+        if DUMMY_MAC_PREFIX in items[1]:
+            dummy_mac_cnt += 1
+            idx = str(snmp_facts['snmp_fdb'][key])
+            assert idx in snmp_facts['snmp_interfaces']
+            assert 'name' in snmp_facts['snmp_interfaces'][idx]
+            if snmp_facts['snmp_interfaces'][idx]['name'] in config_portchannels:
+                recv_portchannels_cnt += 1
+    assert send_cnt == dummy_mac_cnt, "Dummy MAC count does not match"
+    assert send_portchannels_cnt == recv_portchannels_cnt, "Portchannels count does not match"