Esempio n. 1
0
    def wait_for_entry(
        self,
        table_name: str,
        key: str,
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` in the specified table to exist and retrieve it.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being retrieved.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (bool(fv_pairs), fv_pairs)

        message = failure_message or f'Entry not found: key="{key}", table="{table_name}"'
        _, result = wait_for_result(access_function, polling_config, message)

        return result
Esempio n. 2
0
    def wait_for_n_keys(
        self,
        table_name: str,
        num_keys: int,
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> List[str]:
        """Wait for the specified number of keys to exist in the table.

        Args:
            table_name: The name of the table from which to fetch the keys.
            num_keys: The expected number of keys to retrieve from the table.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The keys stored in the table. If no keys are found, then an empty List is returned.
        """
        def access_function():
            keys = self.get_keys(table_name)
            return (len(keys) == num_keys, keys)

        status, result = wait_for_result(
            access_function, self._disable_strict_polling(polling_config))

        if not status:
            message = failure_message or (
                f"Unexpected number of keys: expected={num_keys}, received={len(result)} "
                f'({result}), table="{table_name}"')
            assert not polling_config.strict, message

        return result
Esempio n. 3
0
    def check_swss_ready(self, timeout=300):
        """Verify that SWSS is ready to receive inputs.

        Almost every part of orchagent depends on ports being created and initialized
        before they can proceed with their processing. If we start the tests after orchagent
        has started running but before it has had time to initialize all the ports, then the
        first several tests will fail.
        """
        num_ports = NUM_PORTS

        # Verify that all ports have been initialized and configured
        app_db = self.get_app_db()
        startup_polling_config = PollingConfig(5, timeout, strict=True)

        def _polling_function():
            port_table_keys = app_db.get_keys("PORT_TABLE")
            return ("PortInitDone" in port_table_keys
                    and "PortConfigDone" in port_table_keys, None)

        wait_for_result(_polling_function, startup_polling_config)

        # Verify that all ports have been created
        asic_db = self.get_asic_db()
        asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT",
                                num_ports + 1)  # +1 CPU Port
Esempio n. 4
0
    def wait_for_deleted_entry(
        self,
        table_name: str,
        key: str,
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> Dict[str, str]:
        """Wait for no entry to exist at `key` in the specified table.

        Args:
            table_name: The name of the table being checked.
            key: The key to be checked.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (not bool(fv_pairs), fv_pairs)

        status, result = wait_for_result(
            access_function, self._disable_strict_polling(polling_config))

        if not status:
            message = failure_message or (
                f'Entry still exists: entry={result}, key="{key}", table="{table_name}"'
            )
            assert not polling_config.strict, message

        return result
Esempio n. 5
0
 def _disable_strict_polling(
         polling_config: PollingConfig) -> PollingConfig:
     disabled_config = PollingConfig(
         polling_interval=polling_config.polling_interval,
         timeout=polling_config.timeout,
         strict=False)
     return disabled_config
Esempio n. 6
0
    def wait_for_deleted_keys(
        self,
        table_name: str,
        deleted_keys: List[str],
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> List[str]:
        """Wait for the specfied keys to no longer exist in the table.

        Args:
            table_name: The name of the table from which to fetch the keys.
            deleted_keys: The keys we expect to be removed from the table.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The keys stored in the table. If no keys are found, then an empty List is returned.
        """
        def access_function():
            keys = self.get_keys(table_name)
            return (all(key not in keys for key in deleted_keys), keys)

        status, result = wait_for_result(
            access_function, self._disable_strict_polling(polling_config))

        if not status:
            expected = [key for key in result if key not in deleted_keys]
            message = failure_message or (
                f"Unexpected keys found: expected={expected}, received={result}, "
                f'table="{table_name}"')
            assert not polling_config.strict, message

        return result
Esempio n. 7
0
    def test_AddMaxVlan(self, dvs):
        self.setup_db(dvs)
        max_poll = PollingConfig(polling_interval=3, timeout=300, strict=True)

        min_vid = 2
        max_vid = 4094

        for vlan in range(min_vid, max_vid + 1):
            self.create_vlan(str(vlan))

        self.get_and_verify_vlan_ids(max_vid - 1, polling_config=max_poll)

        for vlan in range(min_vid, max_vid + 1):
            self.remove_vlan(str(vlan))

        self.get_and_verify_vlan_ids(0, polling_config=max_poll)
Esempio n. 8
0
    def wait_for_field_negative_match(
        self,
        table_name: str,
        key: str,
        old_fields: Dict[str, str],
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to have different field/values than `old_fields`.

        This method is useful if you expect some field to change, but you don't know their exact
        values.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            old_fields: The original field/value pairs we expect to change.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (
                all(k in fv_pairs and fv_pairs[k] != v
                    for k, v in old_fields.items()),
                fv_pairs,
            )

        status, result = wait_for_result(
            access_function, self._disable_strict_polling(polling_config))

        if not status:
            message = failure_message or (
                f"Did not expect field/values to match, but they did: provided={old_fields}, "
                f'received={result}, key="{key}", table="{table_name}"')
            assert not polling_config.strict, message

        return result
Esempio n. 9
0
    def _wait_for_gb_asic_db_to_initialize(self) -> None:
        """Wait up to 30 seconds for the default fields to appear in ASIC DB."""
        def _verify_db_contents():
            if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_SWITCH")) != \
               len(self.gearbox.phys):
                return (False, None)

            if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) != \
               2 * len(self.gearbox.interfaces):
                return (False, None)

            if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT_CONNECTOR")) != \
               len(self.gearbox.interfaces):
                return (False, None)

            return (True, None)

        # Verify that GB ASIC DB has been fully initialized
        init_polling_config = PollingConfig(2, 30, strict=True)
        wait_for_result(_verify_db_contents, init_polling_config)
Esempio n. 10
0
    def wait_for_field_match(
        self,
        table_name: str,
        key: str,
        expected_fields: Dict[str, str],
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to have the specified field/values and retrieve it.

        This method is useful if you only care about the contents of a subset of the fields stored
        in the specified entry.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            expected_fields: The fields and their values we expect to see in the entry.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (
                all(fv_pairs.get(k) == v for k, v in expected_fields.items()),
                fv_pairs,
            )

        status, result = wait_for_result(
            access_function, self._disable_strict_polling(polling_config))

        if not status:
            message = failure_message or (
                f"Expected field/value pairs not found: expected={expected_fields}, "
                f'received={result}, key="{key}", table="{table_name}"')
            assert not polling_config.strict, message

        return result
Esempio n. 11
0
    def wait_for_exact_match(
        self,
        table_name: str,
        key: str,
        expected_entry: Dict[str, str],
        polling_config: PollingConfig = PollingConfig(),
        failure_message: str = None,
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to match `expected_entry` and retrieve it.

        This method is useful if you care about *all* the fields stored in the specfied entry.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            expected_entry: The entry we expect to see.
            polling_config: The parameters to use to poll the db.
            failure_message: The message to print if the call times out. This will only take effect
                if the PollingConfig is set to strict.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (fv_pairs == expected_entry, fv_pairs)

        status, result = wait_for_result(
            access_function, self._disable_strict_polling(polling_config))

        if not status:
            message = failure_message or (
                f"Exact match not found: expected={expected_entry}, received={result}, "
                f'key="{key}", table="{table_name}"')
            assert not polling_config.strict, message

        return result
class TestBufferMgrDyn(object):
    DEFAULT_POLLING_CONFIG = PollingConfig(polling_interval=0.01,
                                           timeout=60,
                                           strict=True)

    def setup_db(self, dvs):
        self.initialized = False
        self.cableLenTest1 = "15m"
        self.cableLenTest2 = "25m"
        self.speedToTest1 = "50000"
        self.speedToTest2 = "10000"

        self.app_db = dvs.get_app_db()
        self.asic_db = dvs.get_asic_db()
        self.config_db = dvs.get_config_db()
        self.state_db = dvs.get_state_db()

        fvs = self.config_db.wait_for_entry("PORT", "Ethernet0")
        self.originalSpeed = fvs["speed"]
        if self.originalSpeed == self.speedToTest1:
            self.speedToTest1 = "100000"
        elif self.originalSpeed == self.speedToTest2:
            self.speedToTest2 = "100000"
        elif self.originalSpeed == "":
            self.originalSpeed = "100000"

        # Check whether cable length has been configured
        fvs = self.config_db.wait_for_entry("CABLE_LENGTH", "AZURE")
        self.originalCableLen = fvs["Ethernet0"]
        if self.originalCableLen == self.cableLenTest1:
            self.cableLenTest1 = "20m"
        elif self.originalCableLen == self.cableLenTest2:
            self.cableLenTest2 = "20m"

        fvs = {"mmu_size": "12766208"}
        self.state_db.create_entry("BUFFER_MAX_PARAM_TABLE", "global", fvs)

        # The default lossless priority group will be removed ahead of staring test
        # By doing so all the dynamically generated profiles will be removed and
        # it's easy to identify the SAI OID of the new profile once it's created by
        # comparing the new set of ASIC_STATE:BUFFER_PROFILE table against the initial one
        pgs = self.config_db.get_keys('BUFFER_PG')
        for key in pgs:
            pg = self.config_db.get_entry('BUFFER_PG', key)
            if pg['profile'] == 'NULL':
                self.config_db.delete_entry('BUFFER_PG', key)

        # wait 5 seconds to make sure all the lossless PGs and profiles have been removed
        seconds_delayed = 0
        lossless_profile_name_pattern = 'pg_lossless_([1-9][0-9]*000)_([1-9][0-9]*m)_profile'
        max_delay_seconds = 10
        while seconds_delayed <= max_delay_seconds:
            time.sleep(2)
            seconds_delayed += 2

            lossless_profile = None
            profiles = self.app_db.get_keys('BUFFER_PROFILE_TABLE')
            for key in profiles:
                if re.search(lossless_profile_name_pattern, key):
                    lossless_profile = key
                    break

            if not lossless_profile:
                break

        assert not lossless_profile, \
            "There is still lossless profile ({}) {} seconds after all lossless PGs have been removed".format(lossless_profile, seconds_delayed)

        self.setup_asic_db(dvs)

        self.initialized = True

    def setup_asic_db(self, dvs):
        buffer_pool_set = set(
            self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL"))
        self.initProfileSet = set(
            self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE"))
        self.initPGSet = set(
            self.asic_db.get_keys(
                "ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP"))

        ingress_lossless_pool = self.app_db.get_entry("BUFFER_POOL_TABLE",
                                                      "ingress_lossless_pool")
        self.ingress_lossless_pool_asic = None

        for key in buffer_pool_set:
            bufferpool = self.asic_db.get_entry(
                "ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL", key)
            if bufferpool[
                    "SAI_BUFFER_POOL_ATTR_TYPE"] == "SAI_BUFFER_POOL_TYPE_INGRESS":
                if ingress_lossless_pool["size"] == bufferpool[
                        "SAI_BUFFER_POOL_ATTR_SIZE"]:
                    self.ingress_lossless_pool_asic = bufferpool
                    self.ingress_lossless_pool_oid = key

    def check_new_profile_in_asic_db(self, dvs, profile):
        retry_count = 0
        self.newProfileInAsicDb = None
        while retry_count < 5:
            retry_count += 1
            diff = set(
                self.asic_db.get_keys(
                    "ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")
            ) - self.initProfileSet
            if len(diff) == 1:
                self.newProfileInAsicDb = diff.pop()
                break
            else:
                time.sleep(1)
        assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {} after retry {} times".format(
            profile, retry_count)

        # in case diff is empty, we just treat the newProfileInAsicDb cached the latest one
        fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", profile)
        if fvs.get('dynamic_th'):
            sai_threshold_value = fvs['dynamic_th']
            sai_threshold_mode = 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_DYNAMIC'
        else:
            sai_threshold_value = fvs['static_th']
            sai_threshold_mode = 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_STATIC'
        self.asic_db.wait_for_field_match(
            "ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE",
            self.newProfileInAsicDb, {
                'SAI_BUFFER_PROFILE_ATTR_XON_TH': fvs['xon'],
                'SAI_BUFFER_PROFILE_ATTR_XOFF_TH': fvs['xoff'],
                'SAI_BUFFER_PROFILE_ATTR_RESERVED_BUFFER_SIZE': fvs['size'],
                'SAI_BUFFER_PROFILE_ATTR_POOL_ID':
                self.ingress_lossless_pool_oid,
                'SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE': sai_threshold_mode,
                'SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH':
                sai_threshold_value
            }, self.DEFAULT_POLLING_CONFIG)

    def make_lossless_profile_name(self,
                                   speed,
                                   cable_length,
                                   mtu=None,
                                   dynamic_th=None):
        extra = ""
        if mtu:
            extra += "_mtu" + mtu
        if dynamic_th:
            extra += "_th" + dynamic_th

        return "pg_lossless_" + speed + "_" + cable_length + extra + "_profile"

    def change_cable_length(self, cable_length):
        cable_lengths = self.config_db.get_entry('CABLE_LENGTH', 'AZURE')
        cable_lengths['Ethernet0'] = cable_length
        self.config_db.update_entry('CABLE_LENGTH', 'AZURE', cable_lengths)

    def test_changeSpeed(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        # Change speed to speed1 and verify whether the profile has been updated
        dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1)

        expectedProfile = self.make_lossless_profile_name(
            self.speedToTest1, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # Check whether buffer pg align
        bufferPg = self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove lossless PG
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Change speed to speed2 and verify
        dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest2)
        expectedProfile = self.make_lossless_profile_name(
            self.speedToTest2, self.originalCableLen)

        # Re-add another lossless PG
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6',
                                    {'profile': 'NULL'})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove the lossless PG 6
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # Remove the lossless PG 3-4 and revert speed
        dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed)
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_changeCableLen(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        # Change to new cable length
        self.change_cable_length(self.cableLenTest1)
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove the lossless PGs
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Change to another cable length
        self.change_cable_length(self.cableLenTest2)
        # Check whether the old profile has been removed
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfile)

        # Re-add lossless PGs
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.cableLenTest2)
        # Check the BUFFER_PROFILE_TABLE and BUFFER_PG_TABLE
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Revert the cable length
        self.change_cable_length(self.originalCableLen)
        # Check the BUFFER_PROFILE_TABLE and BUFFER_PG_TABLE
        # we are not able to check whether the SAI OID is removed from ASIC DB here
        # because sometimes the SAI OID in ASIC DB can be reused for the newly created profile
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfile)

        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_MultipleLosslessPg(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        # Add another lossless PG
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6',
                                    {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Change speed and check
        dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1)
        expectedProfile = self.make_lossless_profile_name(
            self.speedToTest1, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Change cable length and check
        self.change_cable_length(self.cableLenTest1)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfile)
        expectedProfile = self.make_lossless_profile_name(
            self.speedToTest1, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Revert the speed and cable length and check
        self.change_cable_length(self.originalCableLen)
        dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfile)
        self.asic_db.wait_for_deleted_entry(
            "ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE",
            self.newProfileInAsicDb)
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove lossless PG 3-4 and 6 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_headroomOverride(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        # Configure static profile
        self.config_db.update_entry(
            'BUFFER_PROFILE', 'test', {
                'xon': '18432',
                'xoff': '16384',
                'size': '34816',
                'dynamic_th': '0',
                'pool': '[BUFFER_POOL|ingress_lossless_pool]'
            })

        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", "test")
        self.app_db.wait_for_exact_match(
            "BUFFER_PROFILE_TABLE", "test", {
                "pool": "[BUFFER_POOL_TABLE:ingress_lossless_pool]",
                "xon": "18432",
                "xoff": "16384",
                "size": "34816",
                "dynamic_th": "0"
            })
        self.check_new_profile_in_asic_db(dvs, "test")

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        self.change_cable_length(self.cableLenTest1)
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # configure lossless PG 3-4 with headroom override
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': '[BUFFER_PROFILE|test]'})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:test]"})
        # configure lossless PG 6 with headroom override
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6',
                                    {'profile': '[BUFFER_PROFILE|test]'})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:test]"})

        # update the profile
        self.config_db.update_entry(
            'BUFFER_PROFILE', 'test', {
                'xon': '18432',
                'xoff': '18432',
                'size': '36864',
                'dynamic_th': '0',
                'pool': '[BUFFER_POOL|ingress_lossless_pool]'
            })
        self.app_db.wait_for_exact_match(
            "BUFFER_PROFILE_TABLE", "test", {
                "pool": "[BUFFER_POOL_TABLE:ingress_lossless_pool]",
                "xon": "18432",
                "xoff": "18432",
                "size": "36864",
                "dynamic_th": "0"
            })
        self.check_new_profile_in_asic_db(dvs, "test")

        # remove all lossless PGs
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # readd lossless PG with dynamic profile
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove the headroom override profile
        self.config_db.delete_entry('BUFFER_PROFILE', 'test')
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", "test")
        self.asic_db.wait_for_deleted_entry(
            "ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE",
            self.newProfileInAsicDb)

        self.change_cable_length(self.originalCableLen)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfile)
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_mtuUpdate(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        test_mtu = '1500'
        default_mtu = '9100'
        expectedProfileMtu = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen, mtu=test_mtu)
        expectedProfileNormal = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)

        # update the mtu on the interface
        dvs.runcmd("config interface mtu Ethernet0 {}".format(test_mtu))

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        self.app_db.wait_for_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfileMtu)
        self.check_new_profile_in_asic_db(dvs, expectedProfileMtu)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {
            "profile":
            "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfileMtu)
        })

        dvs.runcmd("config interface mtu Ethernet0 {}".format(default_mtu))

        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfileMtu)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE",
                                   expectedProfileNormal)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {
            "profile":
            "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfileNormal)
        })

        # clear configuration
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_nonDefaultAlpha(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        test_dynamic_th_1 = '1'
        expectedProfile_th1 = self.make_lossless_profile_name(
            self.originalSpeed,
            self.originalCableLen,
            dynamic_th=test_dynamic_th_1)
        test_dynamic_th_2 = '2'
        expectedProfile_th2 = self.make_lossless_profile_name(
            self.originalSpeed,
            self.originalCableLen,
            dynamic_th=test_dynamic_th_2)

        # add a profile with non-default dynamic_th
        self.config_db.update_entry(
            'BUFFER_PROFILE', 'non-default-dynamic', {
                'dynamic_th': test_dynamic_th_1,
                'headroom_type': 'dynamic',
                'pool': '[BUFFER_POOL|ingress_lossless_pool]'
            })

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry(
            'BUFFER_PG', 'Ethernet0|3-4',
            {'profile': '[BUFFER_PROFILE|non-default-dynamic]'})

        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile_th1)
        self.check_new_profile_in_asic_db(dvs, expectedProfile_th1)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile_th1 + "]"})

        # modify the profile to another dynamic_th
        self.config_db.update_entry(
            'BUFFER_PROFILE', 'non-default-dynamic', {
                'dynamic_th': test_dynamic_th_2,
                'headroom_type': 'dynamic',
                'pool': '[BUFFER_POOL|ingress_lossless_pool]'
            })

        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE",
                                           expectedProfile_th1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile_th2)
        self.check_new_profile_in_asic_db(dvs, expectedProfile_th2)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile_th2 + "]"})

        # clear configuration
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_sharedHeadroomPool(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        # configure lossless PG 3-4 on interface and start up the interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        profileInApplDb = self.app_db.get_entry('BUFFER_PROFILE_TABLE',
                                                expectedProfile)

        # enable shared headroom pool by configuring over subscribe ratio
        default_lossless_buffer_parameter = self.config_db.get_entry(
            'DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE')
        over_subscribe_ratio = default_lossless_buffer_parameter.get(
            'over_subscribe_ratio')
        assert not over_subscribe_ratio or over_subscribe_ratio == '0', "Over subscribe ratio isn't 0"

        # config over subscribe ratio to 2
        default_lossless_buffer_parameter['over_subscribe_ratio'] = '2'
        self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER',
                                    'AZURE', default_lossless_buffer_parameter)

        # check buffer profile: xoff should be removed from size
        profileInApplDb['size'] = profileInApplDb['xon']
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE',
                                         expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # Check ingress_lossless_pool between appldb and asicdb
        # There are only two lossless PGs configured on one port.
        # Hence the shared headroom pool size should be (pg xoff * 2 - private headroom) / over subscribe ratio (2) = xoff - private_headroom / 2.
        ingress_lossless_pool_in_appldb = self.app_db.get_entry(
            'BUFFER_POOL_TABLE', 'ingress_lossless_pool')
        private_headroom = 10 * 1024
        shp_size = str(
            int(profileInApplDb['xoff']) - int(private_headroom / 2))
        ingress_lossless_pool_in_appldb['xoff'] = shp_size
        # toggle shared headroom pool, it requires some time to update pools
        time.sleep(20)
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE',
                                         'ingress_lossless_pool',
                                         ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb = self.asic_db.get_entry(
            'ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE',
            self.ingress_lossless_pool_oid)
        ingress_lossless_pool_in_asicdb[
            'SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size
        self.asic_db.wait_for_field_match(
            'ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL',
            self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)

        # config shared headroom pool size
        shp_size = '204800'
        ingress_lossless_pool_in_configdb = self.config_db.get_entry(
            'BUFFER_POOL', 'ingress_lossless_pool')
        ingress_lossless_pool_in_configdb['xoff'] = shp_size
        self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool',
                                    ingress_lossless_pool_in_configdb)
        # make sure the size is still equal to xon in the profile
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE',
                                         expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # config over subscribe ratio to 4
        default_lossless_buffer_parameter['over_subscribe_ratio'] = '4'
        self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER',
                                    'AZURE', default_lossless_buffer_parameter)
        # shp size wins in case both size and over subscribe ratio is configured
        ingress_lossless_pool_in_appldb['xoff'] = shp_size
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE',
                                         'ingress_lossless_pool',
                                         ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb[
            'SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size
        self.asic_db.wait_for_field_match(
            'ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL',
            self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)
        # make sure the size is still equal to xon in the profile
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE',
                                         expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # remove size configuration, new over subscribe ratio takes effect
        ingress_lossless_pool_in_configdb['xoff'] = '0'
        self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool',
                                    ingress_lossless_pool_in_configdb)
        # shp size: (pg xoff * 2 - private headroom) / over subscribe ratio (4)
        shp_size = str(
            int((2 * int(profileInApplDb['xoff']) - private_headroom) / 4))
        time.sleep(30)
        ingress_lossless_pool_in_appldb['xoff'] = shp_size
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE',
                                         'ingress_lossless_pool',
                                         ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb[
            'SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size
        self.asic_db.wait_for_field_match(
            'ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL',
            self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)
        # make sure the size is still equal to xon in the profile
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE',
                                         expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # remove over subscribe ratio configuration
        default_lossless_buffer_parameter['over_subscribe_ratio'] = '0'
        self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER',
                                    'AZURE', default_lossless_buffer_parameter)
        # check whether shp size has been removed from both asic db and appl db
        ingress_lossless_pool_in_appldb['xoff'] = '0'
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE',
                                         'ingress_lossless_pool',
                                         ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = '0'
        self.asic_db.wait_for_field_match(
            'ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL',
            self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)
        # make sure the size is equal to xon + xoff in the profile
        profileInApplDb['size'] = str(
            int(profileInApplDb['xon']) + int(profileInApplDb['xoff']))
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE',
                                         expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        dvs.runcmd('config interface shutdown Ethernet0')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')

    def test_shutdownPort(self, dvs, testlog):
        self.setup_db(dvs)

        lossy_pg_reference_config_db = '[BUFFER_PROFILE|ingress_lossy_profile]'
        lossy_pg_reference_appl_db = '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]'

        # Startup interface
        dvs.runcmd('config interface startup Ethernet0')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Shutdown port and check whether all the PGs have been removed
        dvs.runcmd("config interface shutdown Ethernet0")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:0")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE", expectedProfile)

        # Add extra lossy and lossless PGs when a port is administratively down
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|1',
                                    {'profile': lossy_pg_reference_config_db})
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6',
                                    {'profile': 'NULL'})

        # Make sure they have not been added to APPL_DB
        time.sleep(30)
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:1")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # Startup port and check whether all the PGs haved been added
        dvs.runcmd("config interface startup Ethernet0")
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:0",
            {"profile": lossy_pg_reference_appl_db})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:1",
            {"profile": lossy_pg_reference_appl_db})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        # Shutdown interface
        dvs.runcmd("config interface shutdown Ethernet0")

    def test_autoNegPort(self, dvs, testlog):
        self.setup_db(dvs)

        advertised_speeds = '10000,25000,50000'
        maximum_advertised_speed = '50000'
        if maximum_advertised_speed == self.originalSpeed:
            # Let's make sure the configured speed isn't equal to maximum advertised speed
            advertised_speeds = '10000,25000'
            maximum_advertised_speed = '25000'

        # Startup interfaces
        dvs.runcmd('config interface startup Ethernet0')

        # Configure lossless PG 3-4 on the interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4',
                                    {'profile': 'NULL'})

        # Enable port auto negotiation
        dvs.runcmd('config interface autoneg Ethernet0 enabled')
        dvs.runcmd('config interface advertised-speeds Ethernet0 {}'.format(
            advertised_speeds))

        # Check the buffer profile. The maximum_advertised_speed should be used
        expectedProfile = self.make_lossless_profile_name(
            maximum_advertised_speed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)})

        # Configure another lossless PG on the interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6',
                                    {'profile': 'NULL'})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)})

        # Disable port auto negotiation
        dvs.runcmd('config interface autoneg Ethernet0 disabled')

        # Check the buffer profile. The configured speed should be used
        expectedProfile = self.make_lossless_profile_name(
            self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:3-4",
            {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)})
        self.app_db.wait_for_field_match(
            "BUFFER_PG_TABLE", "Ethernet0:6",
            {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)})

        # Remove lossless PGs on the interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        # Shutdown interface
        dvs.runcmd('config interface shutdown Ethernet0')
Esempio n. 13
0
    def test_recirc_port(self, dvs):

        # Get port config from configDB
        cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0)
        cfg_port_tbl = swsscommon.Table(cfg_db, swsscommon.CFG_PORT_TABLE_NAME)

        indexes = []
        lanes = []
        keys = cfg_port_tbl.getKeys()
        for port in keys:
            (status, fvs) = cfg_port_tbl.get(port)
            assert(status == True)

            for fv in fvs:
                if fv[0] == "index":
                    indexes.append(int(fv[1]))
                if fv[0] == "lanes":
                    lanes.extend([int(lane) for lane in fv[1].split(",")])

        # Stop swss before modifing the configDB
        dvs.stop_swss()
        time.sleep(1)

        recirc_port_lane_base = max(lanes) + 1
        recirc_port_index_base = max(indexes) + 1

        # Add recirc ports to port config in configDB
        recirc_port_lane_name_map = {}
        for i in range(2):
            name = alias = "Ethernet-Rec%s" % i
            fvs = swsscommon.FieldValuePairs([("role", "Rec" if i % 2 == 0 else "Inb"),
                                              ("alias", alias),
                                              ("lanes", str(recirc_port_lane_base + i)),
                                              ("speed", "10000"),
                                              ("index", str(recirc_port_index_base + i))])
            cfg_port_tbl.set(name, fvs)

        # Start swss
        dvs.start_swss()
        time.sleep(5)

        polling_config = PollingConfig(polling_interval=0.1, timeout=15, strict=True)

        # Verify recirc ports in port table in applDB
        for i in range(2):
            name = alias = "Ethernet-Rec%s" % i
            dvs.get_app_db().wait_for_field_match(swsscommon.APP_PORT_TABLE_NAME, name,
                                                  {"role" : "Rec" if i % 2 == 0 else "Inb",
                                                   "alias" : name,
                                                   "lanes" : str(recirc_port_lane_base + i),
                                                   "speed" : "10000",
                                                   "index" : str(recirc_port_index_base + i) },
                                                  polling_config=polling_config)

        # Verify recirc port lanes in asicDB
        asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0)
        asic_db_lanes_tbl = swsscommon.Table(asic_db, "LANES")

        def _access_function():
            lanes = asic_db_lanes_tbl.get('')[1]
            if len(lanes) == 0:
                return (False, None)

            recirc_port_lanes = [recirc_port_lane_base, recirc_port_lane_base + 1]
            for lane in lanes:
                lane_num = int(lane[0])
                if int(lane_num) in recirc_port_lanes:
                    recirc_port_lanes.remove( lane_num )
            return (not recirc_port_lanes, None)
        wait_for_result(_access_function, polling_config=polling_config)
Esempio n. 14
0
class DVSDatabase:
    """DVSDatabase provides access to redis databases on the virtual switch.

    By default, database operations are configured to use `DEFAULT_POLLING_CONFIG`. Users can
    specify their own PollingConfig, but this shouldn't typically be necessary.
    """

    DEFAULT_POLLING_CONFIG = PollingConfig(polling_interval=0.01,
                                           timeout=5,
                                           strict=True)

    def __init__(self, db_id: int, connector: str):
        """Initialize a DVSDatabase instance.

        Args:
            db_id: The integer ID used to identify the given database instance in redis.
            connector: The I/O connection used to communicate with
                redis (e.g. UNIX socket, TCP socket, etc.).
        """
        self.db_connection = swsscommon.DBConnector(db_id, connector, 0)

    def create_entry(self, table_name: str, key: str,
                     entry: Dict[str, str]) -> None:
        """Add the mapping {`key` -> `entry`} to the specified table.

        Args:
            table_name: The name of the table to add the entry to.
            key: The key that maps to the entry.
            entry: A set of key-value pairs to be stored.
        """
        table = swsscommon.Table(self.db_connection, table_name)
        formatted_entry = swsscommon.FieldValuePairs(list(entry.items()))
        table.set(key, formatted_entry)

    def update_entry(self, table_name: str, key: str,
                     entry: Dict[str, str]) -> None:
        """Update entry of an existing key in the specified table.

        Args:
            table_name: The name of the table.
            key: The key that needs to be updated.
            entry: A set of key-value pairs to be updated.
        """
        table = swsscommon.Table(self.db_connection, table_name)
        formatted_entry = swsscommon.FieldValuePairs(list(entry.items()))
        table.set(key, formatted_entry)

    def get_entry(self, table_name: str, key: str) -> Dict[str, str]:
        """Get the entry stored at `key` in the specified table.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being retrieved.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        table = swsscommon.Table(self.db_connection, table_name)
        (status, fv_pairs) = table.get(key)

        if not status:
            return {}

        return dict(fv_pairs)

    def delete_entry(self, table_name: str, key: str) -> None:
        """Remove the entry stored at `key` in the specified table.

        Args:
            table_name: The name of the table where the entry is being removed.
            key: The key that maps to the entry being removed.
        """
        table = swsscommon.Table(self.db_connection, table_name)
        table._del(key)  # pylint: disable=protected-access

    def get_keys(self, table_name: str) -> List[str]:
        """Get all of the keys stored in the specified table.

        Args:
            table_name: The name of the table from which to fetch the keys.

        Returns:
            The keys stored in the table. If no keys are found, then an empty List is returned.
        """
        table = swsscommon.Table(self.db_connection, table_name)
        keys = table.getKeys()

        return keys if keys else []

    def wait_for_entry(
        self,
        table_name: str,
        key: str,
        polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` in the specified table to exist and retrieve it.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being retrieved.
            polling_config: The parameters to use to poll the db.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def __access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (bool(fv_pairs), fv_pairs)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Entry not found: key=\"{key}\", table=\"{table_name}\""

        return result

    def wait_for_fields(
        self,
        table_name: str,
        key: str,
        expected_fields: List[str],
        polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to have the specified fields and retrieve it.

        This method is useful if you only care about a subset of the fields stored in the
        specified entry.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            expected_fields: The fields that we expect to see in the entry.
            polling_config: The parameters to use to poll the db.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def __access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (all(field in fv_pairs
                        for field in expected_fields), fv_pairs)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Expected fields not found: expected={expected_fields}, \
                received={result}, key=\"{key}\", table=\"{table_name}\""

        return result

    def wait_for_field_match(
        self,
        table_name: str,
        key: str,
        expected_fields: Dict[str, str],
        polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to have the specified field/value pairs and retrieve it.

        This method is useful if you only care about the contents of a subset of the fields stored in the
        specified entry.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            expected_fields: The fields and their values we expect to see in the entry.
            polling_config: The parameters to use to poll the db.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def __access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (all(
                fv_pairs.get(k) == v
                for k, v in expected_fields.items()), fv_pairs)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Expected field/value pairs not found: expected={expected_fields}, \
                received={result}, key=\"{key}\", table=\"{table_name}\""

        return result

    def wait_for_field_negative_match(
        self,
        table_name: str,
        key: str,
        old_fields: Dict[str, str],
        polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to have different field/value pairs than the ones specified.

        This method is useful if you expect some field to change, but you don't know their exact values.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            old_fields: The original field/value pairs we expect to change.
            polling_config: The parameters to use to poll the db.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def __access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (all(k in fv_pairs and fv_pairs[k] != v
                        for k, v in old_fields.items()), fv_pairs)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Did not expect field/values to match, but they did: provided={old_fields}, \
                received={result}, key=\"{key}\", table=\"{table_name}\""

        return result

    def wait_for_exact_match(
        self,
        table_name: str,
        key: str,
        expected_entry: Dict[str, str],
        polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> Dict[str, str]:
        """Wait for the entry stored at `key` to match `expected_entry` and retrieve it.

        This method is useful if you care about *all* the fields stored in the specfied entry.

        Args:
            table_name: The name of the table where the entry is stored.
            key: The key that maps to the entry being checked.
            expected_entry: The entry we expect to see.
            polling_config: The parameters to use to poll the db.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def __access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (fv_pairs == expected_entry, fv_pairs)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Exact match not found: expected={expected_entry}, received={result}, \
                key=\"{key}\", table=\"{table_name}\""

        return result

    def wait_for_deleted_entry(
        self,
        table_name: str,
        key: str,
        polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> Dict[str, str]:
        """Wait for no entry to exist at `key` in the specified table.

        Args:
            table_name: The name of the table being checked.
            key: The key to be checked.
            polling_config: The parameters to use to poll the db.

        Returns:
            The entry stored at `key`. If no entry is found, then an empty Dict is returned.
        """
        def __access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (not bool(fv_pairs), fv_pairs)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Entry still exists: entry={result}, key=\"{key}\", table=\"{table_name}\""

        return result

    def wait_for_n_keys(
            self,
            table_name: str,
            num_keys: int,
            polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> List[str]:
        """Wait for the specified number of keys to exist in the table.

        Args:
            table_name: The name of the table from which to fetch the keys.
            num_keys: The expected number of keys to retrieve from the table.
            polling_config: The parameters to use to poll the db.

        Returns:
            The keys stored in the table. If no keys are found, then an empty List is returned.
        """
        def __access_function():
            keys = self.get_keys(table_name)
            return (len(keys) == num_keys, keys)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Unexpected number of keys: expected={num_keys}, \
                received={len(result)} ({result}), table=\"{table_name}\""

        return result

    def wait_for_matching_keys(
            self,
            table_name: str,
            expected_keys: List[str],
            polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> List[str]:
        """Wait for the specified keys to exist in the table.

        Args:
            table_name: The name of the table from which to fetch the keys.
            expected_keys: The keys we expect to see in the table.
            polling_config: The parameters to use to poll the db.

        Returns:
            The keys stored in the table. If no keys are found, then an empty List is returned.
        """
        def __access_function():
            keys = self.get_keys(table_name)
            return (all(key in keys for key in expected_keys), keys)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                f"Expected keys not found: expected={expected_keys}, received={result}, \
                table=\"{table_name}\""

        return result

    def wait_for_deleted_keys(
            self,
            table_name: str,
            deleted_keys: List[str],
            polling_config: PollingConfig = DEFAULT_POLLING_CONFIG
    ) -> List[str]:
        """Wait for the specfied keys to no longer exist in the table.

        Args:
            table_name: The name of the table from which to fetch the keys.
            deleted_keys: The keys we expect to be removed from the table.
            polling_config: The parameters to use to poll the db.

        Returns:
            The keys stored in the table. If no keys are found, then an empty List is returned.
        """
        def __access_function():
            keys = self.get_keys(table_name)
            return (all(key not in keys for key in deleted_keys), keys)

        status, result = wait_for_result(
            __access_function, self._disable_strict_polling(polling_config))

        if not status:
            expected = [key for key in result if key not in deleted_keys]
            assert not polling_config.strict, \
                f"Unexpected keys found: expected={expected}, received={result}, \
                table=\"{table_name}\""

        return result

    @staticmethod
    def _disable_strict_polling(
            polling_config: PollingConfig) -> PollingConfig:
        disabled_config = PollingConfig(
            polling_interval=polling_config.polling_interval,
            timeout=polling_config.timeout,
            strict=False)
        return disabled_config
Esempio n. 15
0
class DVSDatabase(object):
    """
        DVSDatabase provides access to redis databases on the virtual switch.

        By default, database operations are configured to use
        `DEFAULT_POLLING_CONFIG`. Users can specify their own PollingConfig,
        but this shouldn't typically be necessary.
    """
    DEFAULT_POLLING_CONFIG = PollingConfig(polling_interval=0.01,
                                           timeout=5,
                                           strict=True)

    def __init__(self, db_id, connector):
        """
            Initializes a DVSDatabase instance.

            Args:
                db_id (int): The integer ID used to identify the given database
                    instance in redis.
                connector (str): The I/O connection used to communicate with
                    redis (e.g. unix socket, tcp socket, etc.).
        """

        self.db_connection = swsscommon.DBConnector(db_id, connector, 0)

    def create_entry(self, table_name, key, entry):
        """
            Adds the mapping {`key` -> `entry`} to the specified table.

            Args:
                table_name (str): The name of the table to add the entry to.
                key (str): The key that maps to the entry.
                entry (Dict[str, str]): A set of key-value pairs to be stored.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        formatted_entry = swsscommon.FieldValuePairs(entry.items())
        table.set(key, formatted_entry)

    def update_entry(self, table_name, key, entry):
        """
            Updates entries of an existing key in the specified table.

            Args:
                table_name (str): The name of the table.
                key (str): The key that needs to be updated.
                entry (Dict[str, str]): A set of key-value pairs to be updated.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        formatted_entry = swsscommon.FieldValuePairs(entry.items())
        table.set(key, formatted_entry)

    def get_entry(self, table_name, key):
        """
            Gets the entry stored at `key` in the specified table.

            Args:
                table_name (str): The name of the table where the entry is
                    stored.
                key (str): The key that maps to the entry being retrieved.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        (status, fv_pairs) = table.get(key)

        if not status:
            return {}

        return dict(fv_pairs)

    def delete_entry(self, table_name, key):
        """
            Removes the entry stored at `key` in the specified table.

            Args:
                table_name (str): The name of the table where the entry is
                    being removed.
                key (str): The key that maps to the entry being removed.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        table._del(key)  # pylint: disable=protected-access

    def get_keys(self, table_name):
        """
            Gets all of the keys stored in the specified table.

            Args:
                table_name (str): The name of the table from which to fetch
                the keys.

            Returns:
                List[str]: The keys stored in the table. If no keys are found,
                then an empty List will be returned.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        keys = table.getKeys()

        return keys if keys else []

    def wait_for_entry(self,
                       table_name,
                       key,
                       polling_config=DEFAULT_POLLING_CONFIG):
        """
            Gets the entry stored at `key` in the specified table. This method
            will wait for the entry to exist.

            Args:
                table_name (str): The name of the table where the entry is
                    stored.
                key (str): The key that maps to the entry being retrieved.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.

        """
        def _access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (bool(fv_pairs), fv_pairs)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Entry not found: key=\"{}\", table=\"{}\"".format(key, table_name)

        return result

    def wait_for_field_match(self,
                             table_name,
                             key,
                             expected_fields,
                             polling_config=DEFAULT_POLLING_CONFIG):
        """
            Checks if the provided fields are contained in the entry stored
            at `key` in the specified table. This method will wait for the
            fields to exist.

            Args:
                table_name (str): The name of the table where the entry is
                    stored.
                key (str): The key that maps to the entry being checked.
                expected_fields (dict): The fields and their values we expect
                    to see in the entry.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.
        """
        def _access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (all(
                fv_pairs.get(k) == v
                for k, v in expected_fields.items()), fv_pairs)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Expected fields not found: expected={}, received={}, \
                key=\"{}\", table=\"{}\""                                         .format(expected_fields, result, key, table_name)

        return result

    def wait_for_exact_match(self,
                             table_name,
                             key,
                             expected_entry,
                             polling_config=DEFAULT_POLLING_CONFIG):
        """
            Checks if the provided entry matches the entry stored at `key`
            in the specified table. This method will wait for the exact entry
            to exist.

            Args:
                table_name (str): The name of the table where the entry is
                    stored.
                key (str): The key that maps to the entry being checked.
                expected_entry (dict): The entry we expect to see.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.
        """
        def _access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (fv_pairs == expected_entry, fv_pairs)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Exact match not found: expected={}, received={}, \
                key=\"{}\", table=\"{}\""                                         .format(expected_entry, result, key, table_name)

        return result

    def wait_for_deleted_entry(self,
                               table_name,
                               key,
                               polling_config=DEFAULT_POLLING_CONFIG):
        """
            Checks if there is any entry stored at `key` in the specified
            table. This method will wait for the entry to be empty.

            Args:
                table_name (str): The name of the table being checked.
                key (str): The key to be checked.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.
        """
        def _access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (not bool(fv_pairs), fv_pairs)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Entry still exists: entry={}, key=\"{}\", table=\"{}\""\
                .format(result, key, table_name)

        return result

    def wait_for_n_keys(self,
                        table_name,
                        num_keys,
                        polling_config=DEFAULT_POLLING_CONFIG):
        """
            Gets all of the keys stored in the specified table. This method
            will wait for the specified number of keys.

            Args:
                table_name (str): The name of the table from which to fetch
                    the keys.
                num_keys (int): The expected number of keys to retrieve from
                    the table.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                List[str]: The keys stored in the table. If no keys are found,
                then an empty List will be returned.
        """
        def _access_function():
            keys = self.get_keys(table_name)
            return (len(keys) == num_keys, keys)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Unexpected number of keys: expected={}, received={} ({}), table=\"{}\""\
                .format(num_keys, len(result), result, table_name)

        return result

    def wait_for_matching_keys(self,
                               table_name,
                               expected_keys,
                               polling_config=DEFAULT_POLLING_CONFIG):
        """
            Checks if the specified keys exist in the table. This method
            will wait for the keys to exist.

            Args:
                table_name (str): The name of the table from which to fetch
                    the keys.
                expected_keys (List[str]): The keys we expect to see in the
                    table.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                List[str]: The keys stored in the table. If no keys are found,
                then an empty List will be returned.
        """
        def _access_function():
            keys = self.get_keys(table_name)
            return (all(key in keys for key in expected_keys), keys)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Expected keys not found: expected={}, received={}, table=\"{}\""\
                .format(expected_keys, result, table_name)

        return result

    def wait_for_deleted_keys(self,
                              table_name,
                              deleted_keys,
                              polling_config=DEFAULT_POLLING_CONFIG):
        """
            Checks if the specified keys no longer exist in the table. This
            method will wait for the keys to be deleted.

            Args:
                table_name (str): The name of the table from which to fetch
                    the keys.
                deleted_keys (List[str]): The keys we expect to be removed from
                    the table.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                List[str]: The keys stored in the table. If no keys are found,
                then an empty List will be returned.
        """
        def _access_function():
            keys = self.get_keys(table_name)
            return (all(key not in keys for key in deleted_keys), keys)

        status, result = wait_for_result(
            _access_function, self._disable_strict_polling(polling_config))

        if not status:
            assert not polling_config.strict, \
                "Unexpected keys found: expected={}, received={}, table=\"{}\""\
                .format(deleted_keys, result, table_name)

        return result

    @staticmethod
    def _disable_strict_polling(polling_config):
        disabled_config = PollingConfig(
            polling_interval=polling_config.polling_interval,
            timeout=polling_config.timeout,
            strict=False)
        return disabled_config
Esempio n. 16
0
import pytest
import json
from port_dpb import Port
from port_dpb import DPB
from dvslib.dvs_common import wait_for_result, PollingConfig

ARP_FLUSH_POLLING = PollingConfig(polling_interval=0.01,
                                  timeout=10,
                                  strict=True)
ROUTE_CHECK_POLLING = PollingConfig(polling_interval=0.01,
                                    timeout=5,
                                    strict=True)
"""
Below prefix should be same as the one specified for Ethernet8
in port_breakout_config_db.json in sonic-buildimage/platform/vs/docker-sonic-vs/
"""
Ethernet8_IP = "10.0.0.8/31"
Ethernet8_IPME = "10.0.0.8/32"


@pytest.mark.usefixtures('dpb_setup_fixture')
@pytest.mark.usefixtures('dvs_vlan_manager')
class TestPortDPBSystem(object):
    def create_l3_intf(self, dvs, interface, vrf_name):
        dvs_asic_db = dvs.get_asic_db()
        initial_entries = set(
            dvs_asic_db.get_keys(
                "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE"))

        if interface.startswith("PortChannel"):
            tbl_name = "PORTCHANNEL_INTERFACE"
Esempio n. 17
0
class DVSDatabase(object):
    """
        DVSDatabase provides access to redis databases on the virtual switch.

        By default, database operations are configured to use
        `DEFAULT_POLLING_CONFIG`. Users can specify their own PollingConfig,
        but this shouldn't typically be necessary.
    """
    DEFAULT_POLLING_CONFIG = PollingConfig(polling_interval=0.01,
                                           timeout=5,
                                           strict=True)

    def __init__(self, db_id, connector):
        """
            Initializes a DVSDatabase instance.

            Args:
                db_id (int): The integer ID used to identify the given database
                    instance in redis.
                connector (str): The I/O connection used to communicate with
                    redis (e.g. unix socket, tcp socket, etc.).
        """

        self.db_connection = swsscommon.DBConnector(db_id, connector, 0)

    def create_entry(self, table_name, key, entry):
        """
            Adds the mapping {`key` -> `entry`} to the specified table.

            Args:
                table_name (str): The name of the table to add the entry to.
                key (str): The key that maps to the entry.
                entry (Dict[str, str]): A set of key-value pairs to be stored.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        formatted_entry = swsscommon.FieldValuePairs(entry.items())
        table.set(key, formatted_entry)

    def get_entry(self, table_name, key):
        """
            Gets the entry stored at `key` in the specified table.

            Args:
                table_name (str): The name of the table where the entry is
                    stored.
                key (str): The key that maps to the entry being retrieved.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        (status, fv_pairs) = table.get(key)

        if not status:
            return {}

        return dict(fv_pairs)

    def delete_entry(self, table_name, key):
        """
            Removes the entry stored at `key` in the specified table.

            Args:
                table_name (str): The name of the table where the entry is
                    being removed.
                key (str): The key that maps to the entry being removed.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        table._del(key)  # pylint: disable=protected-access

    def get_keys(self, table_name):
        """
            Gets all of the keys stored in the specified table.

            Args:
                table_name (str): The name of the table from which to fetch
                the keys.

            Returns:
                List[str]: The keys stored in the table. If no keys are found,
                then an empty List will be returned.
        """

        table = swsscommon.Table(self.db_connection, table_name)
        keys = table.getKeys()

        return keys if keys else []

    def wait_for_entry(self,
                       table_name,
                       key,
                       polling_config=DEFAULT_POLLING_CONFIG):
        """
            Gets the entry stored at `key` in the specified table. This method
            will wait for the entry to exist.

            Args:
                table_name (str): The name of the table where the entry is
                    stored.
                key (str): The key that maps to the entry being retrieved.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                Dict[str, str]: The entry stored at `key`. If no entry is found,
                then an empty Dict will be returned.

        """
        def _access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (bool(fv_pairs), fv_pairs)

        return wait_for_result(_access_function, polling_config)

    def wait_for_empty_entry(self,
                             table_name,
                             key,
                             polling_config=DEFAULT_POLLING_CONFIG):
        """
            Checks if there is any entry stored at `key` in the specified
            table. This method will wait for the entry to be empty.

            Args:
                table_name (str): The name of the table being checked.
                key (str): The key to be checked.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                bool: True if no entry exists at `key`, False otherwise.
        """
        def _access_function():
            fv_pairs = self.get_entry(table_name, key)
            return (not fv_pairs, fv_pairs)

        return wait_for_result(_access_function, polling_config)

    def wait_for_n_keys(self,
                        table_name,
                        num_keys,
                        polling_config=DEFAULT_POLLING_CONFIG):
        """
            Gets all of the keys stored in the specified table. This method
            will wait for the specified number of keys.

            Args:
                table_name (str): The name of the table from which to fetch
                    the keys.
                num_keys (int): The expected number of keys to retrieve from
                    the table.
                polling_config (PollingConfig): The parameters to use to poll
                    the db.

            Returns:
                List[str]: The keys stored in the table. If no keys are found,
                then an empty List will be returned.
        """
        def _access_function():
            keys = self.get_keys(table_name)
            return (len(keys) == num_keys, keys)

        return wait_for_result(_access_function, polling_config)
Esempio n. 18
0
class TestBufferMgrDyn(object):
    DEFAULT_POLLING_CONFIG = PollingConfig(polling_interval=0.01, timeout=60, strict=True)
    def setup_db(self, dvs):
        self.initialized = False
        self.cableLenTest1 = "15m"
        self.cableLenTest2 = "25m"
        self.speedToTest1 = "50000"
        self.speedToTest2 = "10000"

        self.app_db = dvs.get_app_db()
        self.asic_db = dvs.get_asic_db()
        self.config_db = dvs.get_config_db()
        self.state_db = dvs.get_state_db()

        fvs = self.config_db.wait_for_entry("PORT", "Ethernet0")
        self.originalSpeed = fvs["speed"]
        if self.originalSpeed == self.speedToTest1:
            self.speedToTest1 = "100000"
        elif self.originalSpeed == self.speedToTest2:
            self.speedToTest2 = "100000"
        elif self.originalSpeed == "":
            self.originalSpeed = "100000"

        # Check whether cable length has been configured
        fvs = self.config_db.wait_for_entry("CABLE_LENGTH", "AZURE")
        self.originalCableLen = fvs["Ethernet0"]
        self.cableLenBeforeTest = self.originalCableLen
        if self.originalCableLen == self.cableLenTest1:
            self.cableLenTest1 = "20m"
        elif self.originalCableLen == self.cableLenTest2:
            self.cableLenTest2 = "20m"
        elif self.originalCableLen == "0m":
            fvs["Ethernet0"] = "5m"
            self.originalCableLen = "5m"
            self.config_db.update_entry("CABLE_LENGTH", "AZURE", fvs)

        fvs = {"mmu_size": "12766208"}
        self.state_db.create_entry("BUFFER_MAX_PARAM_TABLE", "global", fvs)
        self.bufferMaxParameter = self.state_db.wait_for_entry("BUFFER_MAX_PARAM_TABLE", "Ethernet0")

        # The default lossless priority group will be removed ahead of staring test
        # By doing so all the dynamically generated profiles will be removed and
        # it's easy to identify the SAI OID of the new profile once it's created by
        # comparing the new set of ASIC_STATE:BUFFER_PROFILE table against the initial one
        pgs = self.config_db.get_keys('BUFFER_PG')
        for key in pgs:
            pg = self.config_db.get_entry('BUFFER_PG', key)
            if pg['profile'] == 'NULL':
                self.config_db.delete_entry('BUFFER_PG', key)

        # wait 5 seconds to make sure all the lossless PGs and profiles have been removed
        seconds_delayed = 0
        lossless_profile_name_pattern = 'pg_lossless_([1-9][0-9]*000)_([1-9][0-9]*m)_profile'
        max_delay_seconds = 10
        while seconds_delayed <= max_delay_seconds:
            time.sleep(2)
            seconds_delayed += 2

            lossless_profile = None
            profiles = self.app_db.get_keys('BUFFER_PROFILE_TABLE')
            for key in profiles:
                if re.search(lossless_profile_name_pattern, key):
                    lossless_profile = key
                    break

            if not lossless_profile:
                break

        assert not lossless_profile, \
            "There is still lossless profile ({}) {} seconds after all lossless PGs have been removed".format(lossless_profile, seconds_delayed)

        time.sleep(10)

        self.setup_asic_db(dvs)

        self.initialized = True

    def cleanup_db(self, dvs):
        # Clean up: restore the origin cable length
        fvs = self.config_db.wait_for_entry("CABLE_LENGTH", "AZURE")
        fvs["Ethernet0"] = self.cableLenBeforeTest
        self.config_db.update_entry("CABLE_LENGTH", "AZURE", fvs)

    def setup_asic_db(self, dvs):
        buffer_pool_set = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL"))
        self.initProfileSet = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE"))
        self.initPGSet = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP"))

        ingress_lossless_pool = self.app_db.get_entry("BUFFER_POOL_TABLE", "ingress_lossless_pool")
        self.ingress_lossless_pool_asic = None

        for key in buffer_pool_set:
            bufferpool = self.asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL", key)
            if bufferpool["SAI_BUFFER_POOL_ATTR_TYPE"] == "SAI_BUFFER_POOL_TYPE_INGRESS":
                if ingress_lossless_pool["size"] == bufferpool["SAI_BUFFER_POOL_ATTR_SIZE"]:
                    self.ingress_lossless_pool_asic = bufferpool
                    self.ingress_lossless_pool_oid = key

    def check_new_profile_in_asic_db(self, dvs, profile):
        retry_count = 0
        self.newProfileInAsicDb = None
        while retry_count < 5:
            retry_count += 1
            diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet
            if len(diff) == 1:
                self.newProfileInAsicDb = diff.pop()
                break
            else:
                time.sleep(1)
        assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {} after retry {} times".format(profile, retry_count)

        # in case diff is empty, we just treat the newProfileInAsicDb cached the latest one
        fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", profile)
        if fvs.get('dynamic_th'):
            sai_threshold_value = fvs['dynamic_th']
            sai_threshold_mode = 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_DYNAMIC'
        else:
            sai_threshold_value = fvs['static_th']
            sai_threshold_mode = 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_STATIC'
        self.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb,
                                             {'SAI_BUFFER_PROFILE_ATTR_XON_TH': fvs['xon'],
                                              'SAI_BUFFER_PROFILE_ATTR_XOFF_TH': fvs['xoff'],
                                              'SAI_BUFFER_PROFILE_ATTR_RESERVED_BUFFER_SIZE': fvs['size'],
                                              'SAI_BUFFER_PROFILE_ATTR_POOL_ID': self.ingress_lossless_pool_oid,
                                              'SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE': sai_threshold_mode,
                                              'SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH': sai_threshold_value},
                                          self.DEFAULT_POLLING_CONFIG)

    def make_lossless_profile_name(self, speed, cable_length, mtu = None, dynamic_th = None):
        extra = ""
        if mtu:
            extra += "_mtu" + mtu
        if dynamic_th:
            extra += "_th" + dynamic_th

        return "pg_lossless_" + speed + "_" + cable_length + extra + "_profile"

    def change_cable_length(self, cable_length):
        cable_lengths = self.config_db.get_entry('CABLE_LENGTH', 'AZURE')
        cable_lengths['Ethernet0'] = cable_length
        self.config_db.update_entry('CABLE_LENGTH', 'AZURE', cable_lengths)

    def check_queues_after_port_startup(self, dvs):
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "{}:0-2".format("Ethernet0"), {"profile": "egress_lossy_profile"})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "{}:3-4".format("Ethernet0"), {"profile": "egress_lossless_profile"})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "{}:5-6".format("Ethernet0"), {"profile": "egress_lossy_profile"})

    def test_changeSpeed(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')
        self.check_queues_after_port_startup(dvs)

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Change speed to speed1 and verify whether the profile has been updated
        dvs.port_field_set("Ethernet0", "speed", self.speedToTest1)

        expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # Check whether buffer pg align
        bufferPg = self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Remove lossless PG
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Change speed to speed2 and verify
        dvs.port_field_set("Ethernet0", "speed", self.speedToTest2)
        expectedProfile = self.make_lossless_profile_name(self.speedToTest2, self.originalCableLen)

        # Re-add another lossless PG
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Remove the lossless PG 6
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # Remove the lossless PG 3-4 and revert speed
        dvs.port_field_set("Ethernet0", "speed", self.originalSpeed)
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    @pytest.mark.skip(reason="Failing. Under investigation")
    def test_changeCableLen(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Change to new cable length
        self.change_cable_length(self.cableLenTest1)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Remove the lossless PGs
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Change to another cable length
        self.change_cable_length(self.cableLenTest2)
        # Check whether the old profile has been removed
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)

        # Re-add lossless PGs
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.cableLenTest2)
        # Check the BUFFER_PROFILE_TABLE and BUFFER_PG_TABLE
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Revert the cable length
        self.change_cable_length(self.originalCableLen)
        # Check the BUFFER_PROFILE_TABLE and BUFFER_PG_TABLE
        # we are not able to check whether the SAI OID is removed from ASIC DB here
        # because sometimes the SAI OID in ASIC DB can be reused for the newly created profile
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)

        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_MultipleLosslessPg(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Add another lossless PG
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Change speed and check
        dvs.port_field_set("Ethernet0", "speed", self.speedToTest1)
        expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Change cable length and check
        self.change_cable_length(self.cableLenTest1)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Revert the speed and cable length and check
        self.change_cable_length(self.originalCableLen)
        dvs.port_field_set("Ethernet0", "speed", self.originalSpeed)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.asic_db.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Remove lossless PG 3-4 and 6 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_headroomOverride(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        # Configure static profile
        self.config_db.update_entry('BUFFER_PROFILE', 'test',
                                    {'xon': '18432',
                                     'xoff': '16384',
                                     'size': '34816',
                                     'dynamic_th': '0',
                                     'pool': 'ingress_lossless_pool'})

        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", "test")
        self.app_db.wait_for_exact_match("BUFFER_PROFILE_TABLE", "test",
                        { "pool" : "ingress_lossless_pool",
                          "xon" : "18432",
                          "xoff" : "16384",
                          "size" : "34816",
                          "dynamic_th" : "0"
                          })
        self.check_new_profile_in_asic_db(dvs, "test")

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        self.change_cable_length(self.cableLenTest1)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # configure lossless PG 3-4 with headroom override
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'test'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "test"})
        # configure lossless PG 6 with headroom override
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'test'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "test"})

        # update the profile
        self.config_db.update_entry('BUFFER_PROFILE', 'test',
                                    {'xon': '18432',
                                     'xoff': '18432',
                                     'size': '36864',
                                     'dynamic_th': '0',
                                     'pool': 'ingress_lossless_pool'})
        self.app_db.wait_for_exact_match("BUFFER_PROFILE_TABLE", "test",
                        { "pool" : "ingress_lossless_pool",
                          "xon" : "18432",
                          "xoff" : "18432",
                          "size" : "36864",
                          "dynamic_th" : "0"
                          })
        self.check_new_profile_in_asic_db(dvs, "test")

        # remove all lossless PGs
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # readd lossless PG with dynamic profile
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # remove the headroom override profile
        self.config_db.delete_entry('BUFFER_PROFILE', 'test')
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", "test")
        self.asic_db.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb)

        self.change_cable_length(self.originalCableLen)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_mtuUpdate(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        test_mtu = '1500'
        default_mtu = '9100'
        expectedProfileMtu = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, mtu = test_mtu)
        expectedProfileNormal = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)

        # update the mtu on the interface
        dvs.port_field_set("Ethernet0", "mtu", test_mtu)

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        self.app_db.wait_for_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfileMtu)
        self.check_new_profile_in_asic_db(dvs, expectedProfileMtu)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfileMtu})

        dvs.port_field_set("Ethernet0", "mtu", default_mtu)

        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfileMtu)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfileNormal)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfileNormal})

        # clear configuration
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_nonDefaultAlpha(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        test_dynamic_th_1 = '1'
        expectedProfile_th1 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_1)
        test_dynamic_th_2 = '2'
        expectedProfile_th2 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_2)

        # add a profile with non-default dynamic_th
        self.config_db.update_entry('BUFFER_PROFILE', 'non-default-dynamic',
                                    {'dynamic_th': test_dynamic_th_1,
                                     'headroom_type': 'dynamic',
                                     'pool': 'ingress_lossless_pool'})

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'non-default-dynamic'})

        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile_th1)
        self.check_new_profile_in_asic_db(dvs, expectedProfile_th1)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile_th1})

        # modify the profile to another dynamic_th
        self.config_db.update_entry('BUFFER_PROFILE', 'non-default-dynamic',
                                    {'dynamic_th': test_dynamic_th_2,
                                     'headroom_type': 'dynamic',
                                     'pool': 'ingress_lossless_pool'})

        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile_th1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile_th2)
        self.check_new_profile_in_asic_db(dvs, expectedProfile_th2)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile_th2})

        # clear configuration
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_sharedHeadroomPool(self, dvs, testlog):
        self.setup_db(dvs)

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        # configure lossless PG 3-4 on interface and start up the interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        profileInApplDb = self.app_db.get_entry('BUFFER_PROFILE_TABLE', expectedProfile)

        # enable shared headroom pool by configuring over subscribe ratio
        default_lossless_buffer_parameter = self.config_db.get_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE')
        over_subscribe_ratio = default_lossless_buffer_parameter.get('over_subscribe_ratio')
        assert not over_subscribe_ratio or over_subscribe_ratio == '0', "Over subscribe ratio isn't 0"

        # config over subscribe ratio to 2
        default_lossless_buffer_parameter['over_subscribe_ratio'] = '2'
        self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter)

        # check buffer profile: xoff should be removed from size
        profileInApplDb['size'] = profileInApplDb['xon']
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # Check ingress_lossless_pool between appldb and asicdb
        # There are only two lossless PGs configured on one port.
        # Hence the shared headroom pool size should be (pg xoff * 2 - private headroom) / over subscribe ratio (2) = xoff - private_headroom / 2.
        ingress_lossless_pool_in_appldb = self.app_db.get_entry('BUFFER_POOL_TABLE', 'ingress_lossless_pool')
        private_headroom = 10 * 1024
        shp_size = str(int(profileInApplDb['xoff']) - int(private_headroom / 2))
        ingress_lossless_pool_in_appldb['xoff'] = shp_size
        # toggle shared headroom pool, it requires some time to update pools
        time.sleep(20)
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb = self.asic_db.get_entry('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE', self.ingress_lossless_pool_oid)
        ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size
        self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)

        # config shared headroom pool size
        shp_size = '204800'
        ingress_lossless_pool_in_configdb = self.config_db.get_entry('BUFFER_POOL', 'ingress_lossless_pool')
        ingress_lossless_pool_in_configdb['xoff'] = shp_size
        self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool', ingress_lossless_pool_in_configdb)
        # make sure the size is still equal to xon in the profile
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # config over subscribe ratio to 4
        default_lossless_buffer_parameter['over_subscribe_ratio'] = '4'
        self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter)
        # shp size wins in case both size and over subscribe ratio is configured
        ingress_lossless_pool_in_appldb['xoff'] = shp_size
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size
        self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)
        # make sure the size is still equal to xon in the profile
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # remove size configuration, new over subscribe ratio takes effect
        ingress_lossless_pool_in_configdb['xoff'] = '0'
        self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool', ingress_lossless_pool_in_configdb)
        # shp size: (pg xoff * 2 - private headroom) / over subscribe ratio (4)
        shp_size = str(int((2 * int(profileInApplDb['xoff']) - private_headroom) / 4))
        time.sleep(30)
        ingress_lossless_pool_in_appldb['xoff'] = shp_size
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size
        self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)
        # make sure the size is still equal to xon in the profile
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # remove over subscribe ratio configuration
        default_lossless_buffer_parameter['over_subscribe_ratio'] = '0'
        self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter)
        # check whether shp size has been removed from both asic db and appl db
        ingress_lossless_pool_in_appldb['xoff'] = '0'
        self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb)
        ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = '0'
        self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb)
        # make sure the size is equal to xon + xoff in the profile
        profileInApplDb['size'] = str(int(profileInApplDb['xon']) + int(profileInApplDb['xoff']))
        self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        dvs.port_admin_set('Ethernet0', 'down')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_shutdownPort(self, dvs, testlog):
        self.setup_db(dvs)

        lossy_pg_reference_config_db = 'ingress_lossy_profile'
        lossy_pg_reference_appl_db = 'ingress_lossy_profile'
        lossy_queue_reference_config_db = 'egress_lossy_profile'
        lossy_queue_reference_appl_db = 'egress_lossy_profile'
        lossless_queue_reference_appl_db = 'egress_lossless_profile'

        lossy_pg_zero_reference = 'ingress_lossy_pg_zero_profile'
        lossy_queue_zero_reference = 'egress_lossy_zero_profile'
        lossless_queue_zero_reference = 'egress_lossless_zero_profile'

        # Startup interface
        dvs.port_admin_set('Ethernet0', 'up')

        # Configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Shutdown port and check whether zero profiles have been applied on queues and the PG 0
        maximumQueues = int(self.bufferMaxParameter['max_queues']) - 1
        dvs.port_admin_set('Ethernet0', 'down')
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:3-4", {"profile": lossless_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:5-6", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:7-{}".format(maximumQueues), {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE", expectedProfile)

        # Add extra lossy and lossless PGs when a port is administratively down
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|1', {'profile': lossy_pg_reference_config_db})
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'})

        # Add extra queue when a port is administratively down
        self.config_db.update_entry('BUFFER_QUEUE', 'Ethernet0|8', {"profile": lossy_queue_reference_config_db})

        # For queues, the slice in supported but not configured list should be '7-19'.
        # After queue '8' is added, '7-19' should be removed and '7', '8', and '9-19' will be added
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:7-{}".format(maximumQueues))
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:7", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:8", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:9-{}".format(maximumQueues), {"profile": lossy_queue_zero_reference})
        # Make sure they have not been added to APPL_DB
        time.sleep(30)
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:1")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # Startup port and check whether all the PGs have been added
        dvs.port_admin_set('Ethernet0', 'up')
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:1", {"profile": lossy_pg_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:5-6", {"profile": lossy_queue_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:8", {"profile": lossy_queue_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:3-4", {"profile": lossless_queue_reference_appl_db})
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:7")
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:9-{}".format(maximumQueues))

        # Shutdown the port again to verify flow to remove buffer objects from an admin down port
        dvs.port_admin_set('Ethernet0', 'down')
        # First, check whether the objects have been correctly handled
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:3-4", {"profile": lossless_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:5-6", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:7", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:8", {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:9-{}".format(maximumQueues), {"profile": lossy_queue_zero_reference})
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:1")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE", expectedProfile)

        # Remove buffer objects from an admon down port
        self.config_db.delete_entry('BUFFER_QUEUE', 'Ethernet0|8')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|1')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        # Checking
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:7")
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:8")
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:9-{}".format(maximumQueues))
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:7-{}".format(maximumQueues), {"profile": lossy_queue_zero_reference})

        # Startup again
        dvs.port_admin_set('Ethernet0', 'up')
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:3-4", {"profile": lossless_queue_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:5-6", {"profile": lossy_queue_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_reference_appl_db})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:7-{}".format(maximumQueues))

        # Remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_autoNegPort(self, dvs, testlog):
        self.setup_db(dvs)

        advertised_speeds = '10000,25000,50000'
        maximum_advertised_speed = '50000'
        if maximum_advertised_speed == self.originalSpeed:
            # Let's make sure the configured speed isn't equal to maximum advertised speed
            advertised_speeds = '10000,25000'
            maximum_advertised_speed = '25000'

        # Startup interfaces
        dvs.port_admin_set('Ethernet0', 'up')

        # Configure lossless PG 3-4 on the interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Enable port auto negotiation
        dvs.port_field_set('Ethernet0','autoneg', 'on')
        dvs.port_field_set('Ethernet0','adv_speeds', advertised_speeds)

        # Check the buffer profile. The maximum_advertised_speed should be used
        expectedProfile = self.make_lossless_profile_name(maximum_advertised_speed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})

        # Configure another lossless PG on the interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Disable port auto negotiation
        dvs.port_field_set('Ethernet0','autoneg', 'off')

        # Check the buffer profile. The configured speed should be used
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile})

        # Remove lossless PGs on the interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        # Shutdown interface
        dvs.port_admin_set('Ethernet0', 'down')

        self.cleanup_db(dvs)

    def test_removeBufferPool(self, dvs, testlog):
        self.setup_db(dvs)
        # Initialize additional databases that are used by this test only
        self.counter_db = dvs.get_counters_db()
        self.flex_db = dvs.get_flex_db()

        try:
            # Create a new pool
            self.config_db.update_entry('BUFFER_POOL', 'ingress_test_pool', {'size': '0', 'mode': 'static', 'type': 'ingress'})

            # Whether counterpoll is enabled? Enable it if not.
            flex_counter = self.config_db.get_entry("FLEX_COUNTER_TABLE", "BUFFER_POOL_WATERMARK")
            counter_poll_disabled = (not flex_counter or flex_counter["FLEX_COUNTER_STATUS"] != 'enable')
            if counter_poll_disabled:
                self.config_db.update_entry("FLEX_COUNTER_TABLE", "BUFFER_POOL_WATERMARK", {"FLEX_COUNTER_STATUS": "enable"})

            # Check whether counter poll has been enabled
            time.sleep(1)
            poolmap = self.counter_db.wait_for_entry("COUNTERS_BUFFER_POOL_NAME_MAP", "")
            assert poolmap["ingress_test_pool"]
            self.flex_db.wait_for_entry("FLEX_COUNTER_TABLE", "BUFFER_POOL_WATERMARK_STAT_COUNTER:{}".format(poolmap["ingress_test_pool"]))

            self.config_db.delete_entry('BUFFER_POOL', 'ingress_test_pool')
            oid_to_remove = poolmap.pop('ingress_test_pool')
            self.counter_db.wait_for_field_match("COUNTERS_BUFFER_POOL_NAME_MAP", "", poolmap)
            self.flex_db.wait_for_deleted_entry("FLEX_COUNTER_TABLE", "BUFFER_POOL_WATERMARK_STAT_COUNTER:{}".format(oid_to_remove))
        finally:
            # Clean up: disable counterpoll if it was disabled
            if counter_poll_disabled:
                self.config_db.delete_entry("FLEX_COUNTER_TABLE", "BUFFER_POOL_WATERMARK")

        self.cleanup_db(dvs)

    def test_bufferPortMaxParameter(self, dvs, testlog):
        self.setup_db(dvs)

        # Check whether port's maximum parameter has been exposed to STATE_DB
        fvs = self.state_db.wait_for_entry("BUFFER_MAX_PARAM_TABLE", "Ethernet0")
        assert int(fvs["max_queues"]) and int(fvs["max_priority_groups"])

        self.cleanup_db(dvs)
Esempio n. 19
0
class TestBufferMgrDyn(object):
    DEFAULT_POLLING_CONFIG = PollingConfig(polling_interval=0.01, timeout=60, strict=True)
    def setup_db(self, dvs):
        self.initialized = False
        self.cableLenTest1 = "15m"
        self.cableLenTest2 = "25m"
        self.speedToTest1 = "50000"
        self.speedToTest2 = "10000"

        self.app_db = dvs.get_app_db()
        self.asic_db = dvs.get_asic_db()
        self.config_db = dvs.get_config_db()
        self.state_db = dvs.get_state_db()

        fvs = self.config_db.wait_for_entry("PORT", "Ethernet0")
        self.originalSpeed = fvs["speed"]
        if self.originalSpeed == self.speedToTest1:
            self.speedToTest1 = "100000"
        elif self.originalSpeed == self.speedToTest2:
            self.speedToTest2 = "100000"
        elif self.originalSpeed == "":
            self.originalSpeed = "100000"

        # Check whether cable length has been configured
        fvs = self.config_db.wait_for_entry("CABLE_LENGTH", "AZURE")
        self.originalCableLen = fvs["Ethernet0"]
        if self.originalCableLen == self.cableLenTest1:
            self.cableLenTest1 = "20m"
        elif self.originalCableLen == self.cableLenTest2:
            self.cableLenTest2 = "20m"

        fvs = {"mmu_size": "12766208"}
        self.state_db.create_entry("BUFFER_MAX_PARAM_TABLE", "global", fvs)

        # The default lossless priority group will be removed ahead of staring test
        # By doing so all the dynamically generated profiles will be removed and
        # it's easy to identify the SAI OID of the new profile once it's created by
        # comparing the new set of ASIC_STATE:BUFFER_PROFILE table against the initial one
        pgs = self.config_db.get_keys('BUFFER_PG')
        for key in pgs:
            pg = self.config_db.get_entry('BUFFER_PG', key)
            if pg['profile'] == 'NULL':
                self.config_db.delete_entry('BUFFER_PG', key)

        # wait 5 seconds to make sure all the lossless PGs and profiles have been removed
        seconds_delayed = 0
        lossless_profile_name_pattern = 'pg_lossless_([1-9][0-9]*000)_([1-9][0-9]*m)_profile'
        max_delay_seconds = 10
        while seconds_delayed <= max_delay_seconds:
            time.sleep(2)
            seconds_delayed += 2

            lossless_profile = None
            profiles = self.app_db.get_keys('BUFFER_PROFILE_TABLE')
            for key in profiles:
                if re.search(lossless_profile_name_pattern, key):
                    lossless_profile = key
                    break

            if not lossless_profile:
                break

        assert not lossless_profile, \
            "There is still lossless profile ({}) {} seconds after all lossless PGs have been removed".format(lossless_profile, seconds_delayed)

        self.setup_asic_db(dvs)

        self.initialized = True

    def setup_asic_db(self, dvs):
        buffer_pool_set = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL"))
        self.initProfileSet = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE"))
        self.initPGSet = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP"))

        ingress_lossless_pool = self.app_db.get_entry("BUFFER_POOL_TABLE", "ingress_lossless_pool")
        self.ingress_lossless_pool_asic = None

        for key in buffer_pool_set:
            bufferpool = self.asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL", key)
            if bufferpool["SAI_BUFFER_POOL_ATTR_TYPE"] == "SAI_BUFFER_POOL_TYPE_INGRESS":
                if ingress_lossless_pool["size"] == bufferpool["SAI_BUFFER_POOL_ATTR_SIZE"]:
                    self.ingress_lossless_pool_asic = bufferpool
                    self.ingress_lossless_pool_oid = key

    def check_new_profile_in_asic_db(self, dvs, profile):
        diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet
        if len(diff) == 1:
            self.newProfileInAsicDb = diff.pop()
        assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {}".format(profile)

        # in case diff is empty, we just treat the newProfileInAsicDb cached the latest one
        fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", profile)
        if fvs.get('dynamic_th'):
            sai_threshold_value = fvs['dynamic_th']
            sai_threshold_mode = 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_DYNAMIC'
        else:
            sai_threshold_value = fvs['static_th']
            sai_threshold_mode = 'SAI_BUFFER_PROFILE_THRESHOLD_MODE_STATIC'
        self.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb,
                                             {'SAI_BUFFER_PROFILE_ATTR_XON_TH': fvs['xon'],
                                              'SAI_BUFFER_PROFILE_ATTR_XOFF_TH': fvs['xoff'],
                                              'SAI_BUFFER_PROFILE_ATTR_RESERVED_BUFFER_SIZE': fvs['size'],
                                              'SAI_BUFFER_PROFILE_ATTR_POOL_ID': self.ingress_lossless_pool_oid,
                                              'SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE': sai_threshold_mode,
                                              'SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH': sai_threshold_value},
                                          self.DEFAULT_POLLING_CONFIG)

    def make_lossless_profile_name(self, speed, cable_length, mtu = None, dynamic_th = None):
        extra = ""
        if mtu:
            extra += "_mtu" + mtu
        if dynamic_th:
            extra += "_th" + dynamic_th

        return "pg_lossless_" + speed + "_" + cable_length + extra + "_profile"

    def change_cable_length(self, cable_length):
        cable_lengths = self.config_db.get_entry('CABLE_LENGTH', 'AZURE')
        cable_lengths['Ethernet0'] = cable_length
        self.config_db.update_entry('CABLE_LENGTH', 'AZURE', cable_lengths)

    def test_changeSpeed(self, dvs, testlog):
        self.setup_db(dvs)

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Change speed to speed1 and verify whether the profile has been updated
        dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1)

        expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)

        # Check whether buffer pg align
        bufferPg = self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove lossless PG
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Change speed to speed2 and verify
        dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest2)
        expectedProfile = self.make_lossless_profile_name(self.speedToTest2, self.originalCableLen)

        # Re-add another lossless PG
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove the lossless PG 6
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # Remove the lossless PG 3-4 and revert speed
        dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed)
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

    def test_changeCableLen(self, dvs, testlog):
        self.setup_db(dvs)

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Change to new cable length
        self.change_cable_length(self.cableLenTest1)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Remove the lossless PGs
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")

        # Change to another cable length
        self.change_cable_length(self.cableLenTest2)
        # Check whether the old profile has been removed
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)

        # Re-add lossless PGs
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.cableLenTest2)
        # Check the BUFFER_PROFILE_TABLE and BUFFER_PG_TABLE
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # Revert the cable length
        self.change_cable_length(self.originalCableLen)
        # Check the BUFFER_PROFILE_TABLE and BUFFER_PG_TABLE
        # we are not able to check whether the SAI OID is removed from ASIC DB here
        # because sometimes the SAI OID in ASIC DB can be reused for the newly created profile
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)

        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

    def test_MultipleLosslessPg(self, dvs, testlog):
        self.setup_db(dvs)

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        # Add another lossless PG
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'})
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # change speed and check
        dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1)
        expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # change cable length and check
        self.change_cable_length(self.cableLenTest1)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.check_new_profile_in_asic_db(dvs, expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # revert the speed and cable length and check
        self.change_cable_length(self.originalCableLen)
        dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.asic_db.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove lossless PG 3-4 and 6 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

    def test_headroomOverride(self, dvs, testlog):
        self.setup_db(dvs)

        # Configure static profile
        self.config_db.update_entry('BUFFER_PROFILE', 'test',
                                    {'xon': '18432',
                                     'xoff': '16384',
                                     'size': '34816',
                                     'dynamic_th': '0',
                                     'pool': '[BUFFER_POOL|ingress_lossless_pool]'})

        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", "test")
        self.app_db.wait_for_exact_match("BUFFER_PROFILE_TABLE", "test",
                        { "pool" : "[BUFFER_POOL_TABLE:ingress_lossless_pool]",
                          "xon" : "18432",
                          "xoff" : "16384",
                          "size" : "34816",
                          "dynamic_th" : "0"
                          })
        self.check_new_profile_in_asic_db(dvs, "test")

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        self.change_cable_length(self.cableLenTest1)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.cableLenTest1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # configure lossless PG 3-4 with headroom override
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': '[BUFFER_PROFILE|test]'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:test]"})
        # configure lossless PG 6 with headroom override
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': '[BUFFER_PROFILE|test]'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:test]"})

        # update the profile
        self.config_db.update_entry('BUFFER_PROFILE', 'test',
                                    {'xon': '18432',
                                     'xoff': '18432',
                                     'size': '36864',
                                     'dynamic_th': '0',
                                     'pool': '[BUFFER_POOL|ingress_lossless_pool]'})
        self.app_db.wait_for_exact_match("BUFFER_PROFILE_TABLE", "test",
                        { "pool" : "[BUFFER_POOL_TABLE:ingress_lossless_pool]",
                          "xon" : "18432",
                          "xoff" : "18432",
                          "size" : "36864",
                          "dynamic_th" : "0"
                          })
        self.check_new_profile_in_asic_db(dvs, "test")

        # remove all lossless PGs
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6')

        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6")

        # readd lossless PG with dynamic profile
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profie': 'NULL'})
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove the headroom override profile
        self.config_db.delete_entry('BUFFER_PROFILE', 'test')
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", "test")
        self.asic_db.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb)

        self.change_cable_length(self.originalCableLen)
        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"})

        # remove lossless PG 3-4 on interface
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

    def test_mtuUpdate(self, dvs, testlog):
        self.setup_db(dvs)

        test_mtu = '1500'
        default_mtu = '9100'
        expectedProfileMtu = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, mtu = test_mtu)
        expectedProfileNormal = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen)

        # update the mtu on the interface
        dvs.runcmd("config interface mtu Ethernet0 {}".format(test_mtu))

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'})

        self.app_db.wait_for_entry("BUFFER_PG_TABLE", "Ethernet0:3-4")
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfileMtu)
        self.check_new_profile_in_asic_db(dvs, expectedProfileMtu)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfileMtu)})

        dvs.runcmd("config interface mtu Ethernet0 {}".format(default_mtu))

        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfileMtu)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfileNormal)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfileNormal)})

        # clear configuration
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')

    def test_nonDefaultAlpha(self, dvs, testlog):
        self.setup_db(dvs)

        test_dynamic_th_1 = '1'
        expectedProfile_th1 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_1)
        test_dynamic_th_2 = '2'
        expectedProfile_th2 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_2)

        # add a profile with non-default dynamic_th
        self.config_db.update_entry('BUFFER_PROFILE', 'non-default-dynamic',
                                    {'dynamic_th': test_dynamic_th_1,
                                     'headroom_type': 'dynamic',
                                     'pool': '[BUFFER_POOL|ingress_lossless_pool]'})

        # configure lossless PG 3-4 on interface
        self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': '[BUFFER_PROFILE|non-default-dynamic]'})

        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile_th1)
        self.check_new_profile_in_asic_db(dvs, expectedProfile_th1)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile_th1 + "]"})

        # modify the profile to another dynamic_th
        self.config_db.update_entry('BUFFER_PROFILE', 'non-default-dynamic',
                                    {'dynamic_th': test_dynamic_th_2,
                                     'headroom_type': 'dynamic',
                                     'pool': '[BUFFER_POOL|ingress_lossless_pool]'})

        self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile_th1)
        self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile_th2)
        self.check_new_profile_in_asic_db(dvs, expectedProfile_th2)
        self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile_th2 + "]"})

        # clear configuration
        self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4')
        self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic')