def check_deleted_route_entries(self, destinations): def _access_function(): route_entries = self.adb.get_keys( "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") route_destinations = [ json.loads(route_entry)["dest"] for route_entry in route_entries ] return (all(destination not in route_destinations for destination in destinations), None) wait_for_result(_access_function, DVSDatabase.DEFAULT_POLLING_CONFIG)
def check_route_entries_with_vrf(self, destinations, vrf_oids): def _access_function(): route_entries = self.adb.get_keys( "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") route_destination_vrf = [(json.loads(route_entry)["dest"], json.loads(route_entry)["vr"]) for route_entry in route_entries] return (all( (destination, vrf_oid) in route_destination_vrf for destination, vrf_oid in zip(destinations, vrf_oids)), None) wait_for_result(_access_function)
def check_route_entries(self, destinations): def _access_function(): route_entries = self.adb.get_keys( "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") route_destinations = [ json.loads(route_entry)["dest"] for route_entry in route_entries ] return (all(destination in route_destinations for destination in destinations), None) wait_for_result(_access_function)
def check_sub_port_intf_route_entries(self): expected_destinations = [self.IPV4_TOME_UNDER_TEST, self.IPV4_SUBNET_UNDER_TEST, self.IPV6_TOME_UNDER_TEST, self.IPV6_SUBNET_UNDER_TEST] def _access_function(): raw_route_entries = self.asic_db.get_keys(ASIC_ROUTE_ENTRY_TABLE) route_destinations = [str(json.loads(raw_route_entry)["dest"]) for raw_route_entry in raw_route_entries] return (all(dest in route_destinations for dest in expected_destinations), None) wait_for_result(_access_function, DVSDatabase.DEFAULT_POLLING_CONFIG)
def check_inseg_entries(self, present, labels): def _access_function(): inseg_entries = self.adb.get_keys( "ASIC_STATE:SAI_OBJECT_TYPE_INSEG_ENTRY") inseg_labels = [ json.loads(inseg_entry)["label"] for inseg_entry in inseg_entries ] if present: return (all(label in inseg_labels for label in labels), None) else: return (all(label not in inseg_labels for label in labels), None) wait_for_result(_access_function)
def validate_asic_nhg_router_interface(asic_db, ipprefix): def _access_function(): false_ret = (False, '') keys = asic_db.get_keys(ASIC_ROUTE_TB) key = '' route_exists = False for k in keys: rt_key = json.loads(k) if rt_key['dest'] == ipprefix: route_exists = True key = k if not route_exists: return false_ret fvs = asic_db.get_entry(ASIC_ROUTE_TB, key) if not fvs: return false_ret rifid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID") fvs = asic_db.get_entry(ASIC_RIF, rifid) if not fvs: return false_ret return (True, rifid) _, result = wait_for_result( _access_function, failure_message="Route pointing to RIF not found") return result
def validate_asic_nhg_regular_ecmp(asic_db, ipprefix): def _access_function(): false_ret = (False, '') keys = asic_db.get_keys(ASIC_ROUTE_TB) key = '' route_exists = False for k in keys: rt_key = json.loads(k) if rt_key['dest'] == ipprefix: route_exists = True key = k if not route_exists: return false_ret fvs = asic_db.get_entry(ASIC_ROUTE_TB, key) if not fvs: return false_ret nhgid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID") fvs = asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP", nhgid) if not fvs: return false_ret nhg_type = fvs.get("SAI_NEXT_HOP_GROUP_ATTR_TYPE") if nhg_type != "SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP": return false_ret return (True, nhgid) status, result = wait_for_result(_access_function, DVSDatabase.DEFAULT_POLLING_CONFIG) if not status: assert not polling_config.strict, \ f"SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP not found" return result
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 validate_asic_nhg_fine_grained_ecmp(asic_db, ipprefix, size): def _access_function(): false_ret = (False, '') keys = asic_db.get_keys(ASIC_ROUTE_TB) key = '' route_exists = False for k in keys: rt_key = json.loads(k) if rt_key['dest'] == ipprefix: route_exists = True key = k if not route_exists: return false_ret fvs = asic_db.get_entry(ASIC_ROUTE_TB, key) if not fvs: return false_ret nhgid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID") fvs = asic_db.get_entry(ASIC_NHG, nhgid) if not fvs: return false_ret nhg_type = fvs.get("SAI_NEXT_HOP_GROUP_ATTR_TYPE") if nhg_type != "SAI_NEXT_HOP_GROUP_TYPE_FINE_GRAIN_ECMP": return false_ret nhg_cfg_size = fvs.get("SAI_NEXT_HOP_GROUP_ATTR_CONFIGURED_SIZE") if int(nhg_cfg_size) != size: return false_ret return (True, nhgid) _, result = wait_for_result( _access_function, failure_message="Fine Grained ECMP route not found") 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
def validate_asic_nhg_regular_ecmp(asic_db, ipprefix): def _access_function(): false_ret = (False, '') keys = asic_db.get_keys(ASIC_ROUTE_TB) key = '' route_exists = False for k in keys: rt_key = json.loads(k) if rt_key['dest'] == ipprefix: route_exists = True key = k if not route_exists: return false_ret fvs = asic_db.get_entry(ASIC_ROUTE_TB, key) if not fvs: return false_ret nhgid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID") fvs = asic_db.get_entry(ASIC_NHG, nhgid) if not fvs: return false_ret nhg_type = fvs.get("SAI_NEXT_HOP_GROUP_ATTR_TYPE") if nhg_type != "SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP": return false_ret return (True, nhgid) _, result = wait_for_result( _access_function, failure_message= "SAI_NEXT_HOP_GROUP_TYPE_DYNAMIC_UNORDERED_ECMP not found") return result
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_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_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_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 get_ip_prefix_nhg_oid(self, ip_prefix): def _access_function(): route_entry_found = False raw_route_entry_keys = self.asic_db.get_keys( ASIC_ROUTE_ENTRY_TABLE) for raw_route_entry_key in raw_route_entry_keys: route_entry_key = json.loads(raw_route_entry_key) if route_entry_key["dest"] == ip_prefix: route_entry_found = True break return (route_entry_found, raw_route_entry_key) (route_entry_found, raw_route_entry_key) = wait_for_result(_access_function) fvs = self.asic_db.get_entry(ASIC_ROUTE_ENTRY_TABLE, raw_route_entry_key) nhg_oid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID", "") assert nhg_oid != "" assert nhg_oid != "oid:0x0" return nhg_oid
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 check_nexthop(self, present, nhtype, ip, ostype, labels): def _access_function(): nhs = self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") for nh in nhs: fvs = self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP", nh) if fvs.get("SAI_NEXT_HOP_ATTR_TYPE") != nhtype: continue if fvs.get("SAI_NEXT_HOP_ATTR_IP") != ip: continue if nhtype == "SAI_NEXT_HOP_TYPE_IP": return (present, None) if nhtype != "SAI_NEXT_HOP_TYPE_MPLS": continue if fvs.get("SAI_NEXT_HOP_ATTR_OUTSEG_TYPE") != ostype: continue if fvs.get("SAI_NEXT_HOP_ATTR_LABELSTACK") == labels: return (present, None) return (not present, None) wait_for_result(_access_function)
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
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, 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_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)
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 verify_programmed_fg_asic_db_entry(asic_db, nh_memb_exp_count, nh_oid_map, nhgid, bucket_size): def _access_function(): false_ret = (False, None) ret = True nh_memb_count = {} for key in nh_memb_exp_count: nh_memb_count[key] = 0 members = asic_db.get_keys(ASIC_NHG_MEMB) memb_dict = {} for member in members: fvs = asic_db.get_entry(ASIC_NHG_MEMB, member) if fvs == {}: return false_ret index = -1 nh_oid = "0" memb_nhgid = "0" for key, val in fvs.items(): if key == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_INDEX": index = int(val) elif key == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": nh_oid = val elif key == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": memb_nhgid = val if memb_nhgid == "0": print("memb_nhgid was not set") return false_ret if memb_nhgid != nhgid: continue if (index == -1 or nh_oid == "0" or nh_oid_map.get(nh_oid, "NULL") == "NULL" or nh_oid_map.get(nh_oid) not in nh_memb_exp_count): print("Invalid nh: nh_oid " + nh_oid + " index " + str(index)) if nh_oid_map.get(nh_oid, "NULL") == "NULL": print("nh_oid is null") if nh_oid_map.get(nh_oid) not in nh_memb_exp_count: print("nh_memb_exp_count is " + str(nh_memb_exp_count) + " nh_oid_map val is " + nh_oid_map.get(nh_oid)) return false_ret memb_dict[index] = nh_oid_map.get(nh_oid) idxs = [0] * bucket_size for idx, memb in memb_dict.items(): nh_memb_count[memb] = 1 + nh_memb_count[memb] idxs[idx] = idxs[idx] + 1 for key in nh_memb_exp_count: ret = ret and (nh_memb_count[key] == nh_memb_exp_count[key]) for idx in idxs: ret = ret and (idx == 1) if ret != True: print("Expected member count was " + str(nh_memb_exp_count) + " Received was " + str(nh_memb_count)) print("Indexes arr was " + str(idxs)) return (ret, nh_memb_count) status, result = wait_for_result(_access_function) assert status, f"Exact match not found: expected={nh_memb_exp_count}, received={result}" 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 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 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_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 check_route_entries_nexthop(self, destinations, vrf_oids, nexthops): def _access_function_nexthop(): nexthop_entries = self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") nexthop_oids = dict([(self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP", key)["SAI_NEXT_HOP_ATTR_IP"], key) for key in nexthop_entries]) return (all(nexthop in nexthop_oids for nexthop in nexthops), nexthop_oids) status, nexthop_oids = wait_for_result(_access_function_nexthop) def _access_function_route_nexthop(): route_entries = self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") route_destination_nexthop = dict([((json.loads(route_entry)["dest"], json.loads(route_entry)["vr"]), self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", route_entry).get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID")) for route_entry in route_entries]) return (all(route_destination_nexthop.get((destination, vrf_oid)) == nexthop_oids.get(nexthop) for destination, vrf_oid, nexthop in zip(destinations, vrf_oids, nexthops)), None) wait_for_result(_access_function_route_nexthop)