33import json
44import os
55import datetime
6+ from typing import List , Optional
67from collections import defaultdict
7- from tests .common .fixtures .consistency_checker .constants import SUPPORTED_PLATFORMS_AND_VERSIONS
8+ from tests .common .fixtures .consistency_checker .constants import SUPPORTED_PLATFORMS_AND_VERSIONS , \
9+ ConsistencyCheckQueryKey , ALL_ATTRIBUTES
810
911logger = logging .getLogger (__name__ )
1012
@@ -154,7 +156,7 @@ def get_db_and_asic_peers(self, keys=["*"]) -> dict:
154156
155157 return dict (results )
156158
157- def check_consistency (self , keys = [ "*" ] ) -> dict :
159+ def check_consistency (self , keys = None ) -> dict :
158160 """
159161 Get the out-of-sync ASIC_DB and ASIC attributes. Differences are indicative of an error state.
160162 Same arg style as the get_objects function but returns a list of objects that don't match or couldn't
@@ -163,7 +165,7 @@ def check_consistency(self, keys=["*"]) -> dict:
163165
164166 :param keys: Optional list of glob search strings that correspond to the --key arg of sonic-db-dump.
165167 sonic-db-dump doesn't take multiple keys, so a list is passed in to support multiple
166- keys at the API level.
168+ keys at the API level. If not provided, then the default keys are used.
167169 :return: Dictionary containing the out-of-sync ASIC_DB and ASIC attributes.
168170
169171 Example return val (matching):
@@ -186,19 +188,25 @@ def check_consistency(self, keys=["*"]) -> dict:
186188 "failedToQueryAsic": [
187189 {"SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH": "Failed to query attribute value"}
188190 ],
189- "mismatchedAttributes": ["SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE"]
191+ "mismatchedAttributes": ["SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE"],
192+ "attributeNotImplemented": ["SAI_BUFFER_PROFILE_ATTR_POOL_ID"]
190193 },
191194 ...
192195 }
193196 """
197+ if keys is None :
198+ platform = self ._duthost .facts ['platform' ]
199+ os_version = self ._duthost .image_facts ()["ansible_facts" ]["ansible_image_facts" ]["current" ]
200+ keys = self ._get_consistency_checker_keys (platform , os_version )
194201
195202 db_attributes = self ._get_db_attributes (keys )
196203 asic_attributes = self ._get_asic_attributes_from_db_results (db_attributes )
197204
198205 inconsistencies = defaultdict (lambda : {
199206 "attributes" : {},
200207 "failedToQueryAsic" : [],
201- "mismatchedAttributes" : []
208+ "mismatchedAttributes" : [],
209+ "attributeNotImplemented" : [],
202210 })
203211
204212 for object in db_attributes :
@@ -231,23 +239,71 @@ def check_consistency(self, keys=["*"]) -> dict:
231239 if asic_query_success :
232240 inconsistencies [object ]["mismatchedAttributes" ].append (attr )
233241 else :
234- inconsistencies [object ]["failedToQueryAsic" ].append ({attr : asic_object [attr ]["error" ]})
242+ error = asic_object [attr ]["error" ]
243+ if "ATTR_NOT_IMPLEMENTED" in error :
244+ inconsistencies [object ]["attributeNotImplemented" ].append (attr )
245+ else :
246+ inconsistencies [object ]["failedToQueryAsic" ].append ({attr : error })
235247
236248 return dict (inconsistencies )
237249
238- def _get_db_attributes (self , keys : list ) -> dict :
250+ def _get_consistency_checker_keys (self , platform , os_version ) -> List [str ]:
251+ """
252+ Get the keys for the given platform and OS version.
253+
254+ :param platform: Platform name
255+ :param os_version: OS version
256+ :return: List of keys
257+ """
258+
259+ if platform not in SUPPORTED_PLATFORMS_AND_VERSIONS :
260+ raise Exception (f"Unsupported platform: { platform } " )
261+
262+ supported_versions = SUPPORTED_PLATFORMS_AND_VERSIONS [platform ]
263+ for version in supported_versions :
264+ if version in os_version :
265+ return supported_versions [version ]
266+
267+ raise Exception (f"Unsupported OS version: { os_version } " )
268+
269+ def _get_db_attributes (self , keys : List [ConsistencyCheckQueryKey ]) -> dict :
239270 """
240271 Fetchs and merges the attributes of the objects returned by the search key from the DB.
241272 """
242273 db_attributes = {}
243274 for key in keys :
244- result = self ._duthost .command (f"sonic-db-dump -k '{ key } ' -n ASIC_DB" )
275+ result = self ._duthost .command (f"sonic-db-dump -k '{ key . key } ' -n ASIC_DB" )
245276 if result ['rc' ] != 0 :
246277 raise Exception ((f"Failed to fetch attributes for key '{ key } ' from ASIC_DB. "
247278 f"Return code: { result ['rc' ]} , stdout: { result ['stdout' ]} , "
248279 f"stderr: { result ['stderr' ]} " ))
249280
250281 query_result = json .loads (result ['stdout' ])
282+
283+ # Filter for attributes that we want ...
284+ objects_with_no_attrs = []
285+ for object in query_result :
286+
287+ if "NULL" in query_result [object ]["value" ]:
288+ logger .debug (f"Ignoring attribute 'NULL' for object '{ object } '" )
289+ del query_result [object ]["value" ]["NULL" ]
290+
291+ if ALL_ATTRIBUTES in key .attributes :
292+ logger .debug (f"Retaining all attributes for object '{ object } '" )
293+ else :
294+ attributes_to_remove = set (query_result [object ]["value" ].keys ()) - set (key .attributes )
295+ for attr in attributes_to_remove :
296+ logger .debug (f"Ignoring attribute '{ attr } ' for object '{ object } '" )
297+ del query_result [object ]["value" ][attr ]
298+
299+ if len (query_result [object ]["value" ]) == 0 :
300+ objects_with_no_attrs .append (object )
301+
302+ # ... then remove the objects that have no attributes left
303+ for object in objects_with_no_attrs :
304+ logger .debug (f"Ignoring empty object '{ object } '" )
305+ del query_result [object ]
306+
251307 db_attributes .update (query_result )
252308
253309 return db_attributes
@@ -304,6 +360,19 @@ def _get_asic_attributes_from_db_results(self, db_attributes: dict) -> dict:
304360
305361
306362class ConsistencyCheckerProvider :
363+
364+ def __init__ (self , libsairedis_url_template : Optional [str ],
365+ python3_pysairedis_url_template : Optional [str ]) -> None :
366+ """
367+ The libsairedis_url_template and python3_pysairedis_url_template are optional URL templates that the
368+ consistency checker can use to download the libsairedis and python3-pysairedis debs respectively.
369+
370+ :param libsairedis_url_template: Optional URL template for the libsairedis deb
371+ :param python3_pysairedis_url_template: Optional URL template for the python3-pysairedis deb
372+ """
373+ self ._libsairedis_url_template = libsairedis_url_template
374+ self ._python3_pysairedis_url_template = python3_pysairedis_url_template
375+
307376 def is_consistency_check_supported (self , dut ) -> bool :
308377 """
309378 Checks if the provided DUT is supported for consistency checking.
@@ -318,29 +387,56 @@ def is_consistency_check_supported(self, dut) -> bool:
318387
319388 current_version = dut .image_facts ()['ansible_facts' ]['ansible_image_facts' ]['current' ]
320389 supported_versions = SUPPORTED_PLATFORMS_AND_VERSIONS [platform ]
321- if any (v in current_version for v in supported_versions ):
390+ if any (v in current_version for v in supported_versions . keys () ):
322391 return True
323392
324393 return False
325394
326- def get_consistency_checker (self , dut , libsairedis_download_url = None ,
327- python3_pysairedis_download_url = None ) -> ConsistencyChecker :
395+ def get_consistency_checker (self , dut ) -> ConsistencyChecker :
328396 """
329397 Get a new instance of the ConsistencyChecker class.
330398
331399 :param dut: SonicHost object
332- :param libsairedis_download_url: Optional URL that the consistency checker should use to download the
333- libsairedis deb
334- :param python3_pysairedis_download_url: Optional URL that the consistency checker should use to
335- download the python3-pysairedis deb
336400 :return ConsistencyChecker: New instance of the ConsistencyChecker class
337401 """
402+
403+ os_version = dut .image_facts ()["ansible_facts" ]["ansible_image_facts" ]["current" ]
404+
405+ if self ._libsairedis_url_template or self ._python3_pysairedis_url_template :
406+ if "202305" in os_version :
407+ sonic_version_template_param = "202305"
408+ elif "202311" in os_version :
409+ sonic_version_template_param = "202311"
410+ else :
411+ raise Exception (f"Unsupported OS version: { os_version } " )
412+
413+ libsairedis_download_url = self ._libsairedis_url_template \
414+ .format (sonic_version = sonic_version_template_param )\
415+ if self ._libsairedis_url_template else None
416+
417+ python3_pysairedis_download_url = self ._python3_pysairedis_url_template \
418+ .format (sonic_version = sonic_version_template_param )\
419+ if self ._python3_pysairedis_url_template else None
420+
338421 return ConsistencyChecker (dut , libsairedis_download_url , python3_pysairedis_download_url )
339422
340423
341424@pytest .fixture
342- def consistency_checker_provider ():
425+ def consistency_checker_provider (request ):
343426 """
344427 Fixture that provides the ConsistencyCheckerProvider class.
428+
429+ :param request: pytest request object
345430 """
346- return ConsistencyCheckerProvider ()
431+
432+ if not request .config .getoption ("enable_consistency_checker" ):
433+ logger .info ("Consistency checker is not enabled. Skipping check." )
434+ return None
435+
436+ consistency_checker_libsairedis_url_template = request .config .getoption (
437+ "consistency_checker_libsairedis_url_template" )
438+ consistency_checker_python3_pysairedis_url_template = request .config .getoption (
439+ "consistency_checker_python3_pysairedis_url_template" )
440+
441+ return ConsistencyCheckerProvider (consistency_checker_libsairedis_url_template ,
442+ consistency_checker_python3_pysairedis_url_template )
0 commit comments