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
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
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
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
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
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
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)
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
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)
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
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')
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)
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
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
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"
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)
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)
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')