1e7e9171eSGeorge Keishing#!/usr/bin/env python3 2f2613b7aSGeorge Keishing 3f2613b7aSGeorge Keishingr""" 4f2613b7aSGeorge KeishingBMC redfish utility functions. 5f2613b7aSGeorge Keishing""" 6f2613b7aSGeorge Keishing 7f2613b7aSGeorge Keishingimport json 83a6f0738SGeorge Keishingimport re 920f38712SPatrick Williams 10e635ddc0SGeorge Keishingimport gen_print as gp 1120f38712SPatrick Williamsfrom robot.libraries.BuiltIn import BuiltIn 12f2613b7aSGeorge Keishing 1305aa70bcSTony LeeMTLS_ENABLED = BuiltIn().get_variable_value("${MTLS_ENABLED}") 1405aa70bcSTony Lee 15f2613b7aSGeorge Keishing 16f2613b7aSGeorge Keishingclass bmc_redfish_utils(object): 1720f38712SPatrick Williams ROBOT_LIBRARY_SCOPE = "TEST SUITE" 18eb1fe352SGeorge Keishing 19f2613b7aSGeorge Keishing def __init__(self): 20f2613b7aSGeorge Keishing r""" 21f2613b7aSGeorge Keishing Initialize the bmc_redfish_utils object. 22f2613b7aSGeorge Keishing """ 23f2613b7aSGeorge Keishing # Obtain a reference to the global redfish object. 24eb1fe352SGeorge Keishing self.__inited__ = False 2520f38712SPatrick Williams self._redfish_ = BuiltIn().get_library_instance("redfish") 26f2613b7aSGeorge Keishing 2720f38712SPatrick Williams if MTLS_ENABLED == "True": 2805aa70bcSTony Lee self.__inited__ = True 2905aa70bcSTony Lee else: 30eb1fe352SGeorge Keishing # There is a possibility that a given driver support both redfish and 31eb1fe352SGeorge Keishing # legacy REST. 32eb1fe352SGeorge Keishing self._redfish_.login() 3320f38712SPatrick Williams self._rest_response_ = self._redfish_.get( 3420f38712SPatrick Williams "/xyz/openbmc_project/", valid_status_codes=[200, 404] 3520f38712SPatrick Williams ) 36eb1fe352SGeorge Keishing 37eb1fe352SGeorge Keishing # If REST URL /xyz/openbmc_project/ is supported. 38eb1fe352SGeorge Keishing if self._rest_response_.status == 200: 39eb1fe352SGeorge Keishing self.__inited__ = True 40eb1fe352SGeorge Keishing 4120f38712SPatrick Williams BuiltIn().set_global_variable( 4220f38712SPatrick Williams "${REDFISH_REST_SUPPORTED}", self.__inited__ 4320f38712SPatrick Williams ) 44eb1fe352SGeorge Keishing 45374e684cSGeorge Keishing def get_redfish_session_info(self): 46374e684cSGeorge Keishing r""" 47374e684cSGeorge Keishing Returns redfish sessions info dictionary. 48374e684cSGeorge Keishing 49374e684cSGeorge Keishing { 50374e684cSGeorge Keishing 'key': 'yLXotJnrh5nDhXj5lLiH' , 51374e684cSGeorge Keishing 'location': '/redfish/v1/SessionService/Sessions/nblYY4wlz0' 52374e684cSGeorge Keishing } 53374e684cSGeorge Keishing """ 54374e684cSGeorge Keishing session_dict = { 5597c93945SGeorge Keishing "key": self._redfish_.get_session_key(), 5620f38712SPatrick Williams "location": self._redfish_.get_session_location(), 57374e684cSGeorge Keishing } 58374e684cSGeorge Keishing return session_dict 59374e684cSGeorge Keishing 6037122b63SSandhya Somashekar def get_attribute(self, resource_path, attribute, verify=None): 61f2613b7aSGeorge Keishing r""" 62f2613b7aSGeorge Keishing Get resource attribute. 63f2613b7aSGeorge Keishing 64f2613b7aSGeorge Keishing Description of argument(s): 65c86a2f72SMichael Walsh resource_path URI resource absolute path (e.g. 66c86a2f72SMichael Walsh "/redfish/v1/Systems/1"). 67f2613b7aSGeorge Keishing attribute Name of the attribute (e.g. 'PowerState'). 68f2613b7aSGeorge Keishing """ 69f2613b7aSGeorge Keishing 70f2613b7aSGeorge Keishing resp = self._redfish_.get(resource_path) 7137122b63SSandhya Somashekar 7237122b63SSandhya Somashekar if verify: 7337122b63SSandhya Somashekar if resp.dict[attribute] == verify: 7437122b63SSandhya Somashekar return resp.dict[attribute] 7537122b63SSandhya Somashekar else: 7637122b63SSandhya Somashekar raise ValueError("Attribute value is not equal") 7737122b63SSandhya Somashekar elif attribute in resp.dict: 78f2613b7aSGeorge Keishing return resp.dict[attribute] 79f2613b7aSGeorge Keishing 80f2613b7aSGeorge Keishing return None 81f2613b7aSGeorge Keishing 82c3c05c2bSGeorge Keishing def get_properties(self, resource_path): 83c3c05c2bSGeorge Keishing r""" 84c3c05c2bSGeorge Keishing Returns dictionary of attributes for the resource. 85c3c05c2bSGeorge Keishing 86c3c05c2bSGeorge Keishing Description of argument(s): 87c86a2f72SMichael Walsh resource_path URI resource absolute path (e.g. 8837122b63SSandhya Somashekar /redfish/v1/Systems/1"). 89c3c05c2bSGeorge Keishing """ 90c3c05c2bSGeorge Keishing 91c3c05c2bSGeorge Keishing resp = self._redfish_.get(resource_path) 92c3c05c2bSGeorge Keishing return resp.dict 93c3c05c2bSGeorge Keishing 94789c3b4cSGeorge Keishing def get_members_uri(self, resource_path, attribute): 95789c3b4cSGeorge Keishing r""" 96789c3b4cSGeorge Keishing Returns the list of valid path which has a given attribute. 97789c3b4cSGeorge Keishing 98789c3b4cSGeorge Keishing Description of argument(s): 99789c3b4cSGeorge Keishing resource_path URI resource base path (e.g. 100789c3b4cSGeorge Keishing '/redfish/v1/Systems/', 101789c3b4cSGeorge Keishing '/redfish/v1/Chassis/'). 102789c3b4cSGeorge Keishing attribute Name of the attribute (e.g. 'PowerSupplies'). 103789c3b4cSGeorge Keishing """ 104789c3b4cSGeorge Keishing 105d5f179e2SGeorge Keishing # Set quiet variable to keep subordinate get() calls quiet. 106d5f179e2SGeorge Keishing quiet = 1 107d5f179e2SGeorge Keishing 108789c3b4cSGeorge Keishing # Get the member id list. 109789c3b4cSGeorge Keishing # e.g. ['/redfish/v1/Chassis/foo', '/redfish/v1/Chassis/bar'] 110789c3b4cSGeorge Keishing resource_path_list = self.get_member_list(resource_path) 111789c3b4cSGeorge Keishing 112789c3b4cSGeorge Keishing valid_path_list = [] 113789c3b4cSGeorge Keishing 114789c3b4cSGeorge Keishing for path_idx in resource_path_list: 115789c3b4cSGeorge Keishing # Get all the child object path under the member id e.g. 116789c3b4cSGeorge Keishing # ['/redfish/v1/Chassis/foo/Power','/redfish/v1/Chassis/bar/Power'] 117789c3b4cSGeorge Keishing child_path_list = self.list_request(path_idx) 118789c3b4cSGeorge Keishing 119789c3b4cSGeorge Keishing # Iterate and check if path object has the attribute. 120789c3b4cSGeorge Keishing for child_path_idx in child_path_list: 12120f38712SPatrick Williams if ( 12220f38712SPatrick Williams ("JsonSchemas" in child_path_idx) 12320f38712SPatrick Williams or ("SessionService" in child_path_idx) 12420f38712SPatrick Williams or ("#" in child_path_idx) 12520f38712SPatrick Williams ): 1266396bc6dSGeorge Keishing continue 127789c3b4cSGeorge Keishing if self.get_attribute(child_path_idx, attribute): 128789c3b4cSGeorge Keishing valid_path_list.append(child_path_idx) 129789c3b4cSGeorge Keishing 130d5f179e2SGeorge Keishing BuiltIn().log_to_console(valid_path_list) 131789c3b4cSGeorge Keishing return valid_path_list 132789c3b4cSGeorge Keishing 1333a6f0738SGeorge Keishing def get_endpoint_path_list(self, resource_path, end_point_prefix): 1343a6f0738SGeorge Keishing r""" 1353a6f0738SGeorge Keishing Returns list with entries ending in "/endpoint". 1363a6f0738SGeorge Keishing 1373a6f0738SGeorge Keishing Description of argument(s): 1383a6f0738SGeorge Keishing resource_path URI resource base path (e.g. "/redfish/v1/Chassis/"). 139e68cbfb3SGeorge Keishing end_point_prefix Name of the endpoint (e.g. 'Power'). 1403a6f0738SGeorge Keishing 1413a6f0738SGeorge Keishing Find all list entries ending in "/endpoint" combination such as 1423a6f0738SGeorge Keishing /redfish/v1/Chassis/<foo>/Power 1433a6f0738SGeorge Keishing /redfish/v1/Chassis/<bar>/Power 1443a6f0738SGeorge Keishing """ 1453a6f0738SGeorge Keishing 1463a6f0738SGeorge Keishing end_point_list = self.list_request(resource_path) 1473a6f0738SGeorge Keishing 1483a6f0738SGeorge Keishing # Regex to match entries ending in "/prefix" with optional underscore. 149d2f210a0SGeorge Keishing regex = ".*/" + end_point_prefix + "[_]?[0-9]*$" 1503a6f0738SGeorge Keishing return [x for x in end_point_list if re.match(regex, x, re.IGNORECASE)] 1513a6f0738SGeorge Keishing 1527ec45937SGeorge Keishing def get_target_actions(self, resource_path, target_attribute): 1537ec45937SGeorge Keishing r""" 1547ec45937SGeorge Keishing Returns resource target entry of the searched target attribute. 1557ec45937SGeorge Keishing 1567ec45937SGeorge Keishing Description of argument(s): 1577ec45937SGeorge Keishing resource_path URI resource absolute path 1587ec45937SGeorge Keishing (e.g. "/redfish/v1/Systems/system"). 1597ec45937SGeorge Keishing target_attribute Name of the attribute (e.g. 'ComputerSystem.Reset'). 1607ec45937SGeorge Keishing 1617ec45937SGeorge Keishing Example: 1627ec45937SGeorge Keishing "Actions": { 1637ec45937SGeorge Keishing "#ComputerSystem.Reset": { 1647ec45937SGeorge Keishing "ResetType@Redfish.AllowableValues": [ 1657ec45937SGeorge Keishing "On", 1667ec45937SGeorge Keishing "ForceOff", 1677ec45937SGeorge Keishing "GracefulRestart", 1687ec45937SGeorge Keishing "GracefulShutdown" 1697ec45937SGeorge Keishing ], 1707ec45937SGeorge Keishing "target": "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset" 1717ec45937SGeorge Keishing } 1727ec45937SGeorge Keishing } 1737ec45937SGeorge Keishing """ 1747ec45937SGeorge Keishing 1757ec45937SGeorge Keishing global target_list 1767ec45937SGeorge Keishing target_list = [] 1777ec45937SGeorge Keishing 1787ec45937SGeorge Keishing resp_dict = self.get_attribute(resource_path, "Actions") 1797ec45937SGeorge Keishing if resp_dict is None: 1807ec45937SGeorge Keishing return None 1817ec45937SGeorge Keishing 1827ec45937SGeorge Keishing # Recursively search the "target" key in the nested dictionary. 1837ec45937SGeorge Keishing # Populate the target_list of target entries. 1847ec45937SGeorge Keishing self.get_key_value_nested_dict(resp_dict, "target") 1857ec45937SGeorge Keishing # Return the matching target URL entry. 1867ec45937SGeorge Keishing for target in target_list: 1877ec45937SGeorge Keishing # target "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset" 18820f38712SPatrick Williams attribute_in_uri = target.rsplit("/", 1)[-1] 189adfdb602SAnusha Dathatri # attribute_in_uri "ComputerSystem.Reset" 190adfdb602SAnusha Dathatri if target_attribute == attribute_in_uri: 1917ec45937SGeorge Keishing return target 1927ec45937SGeorge Keishing 1937ec45937SGeorge Keishing return None 1947ec45937SGeorge Keishing 195dabf38f1SGeorge Keishing def get_member_list(self, resource_path): 196dabf38f1SGeorge Keishing r""" 197dabf38f1SGeorge Keishing Perform a GET list request and return available members entries. 198dabf38f1SGeorge Keishing 199dabf38f1SGeorge Keishing Description of argument(s): 200dabf38f1SGeorge Keishing resource_path URI resource absolute path 201dabf38f1SGeorge Keishing (e.g. "/redfish/v1/SessionService/Sessions"). 202dabf38f1SGeorge Keishing 203dabf38f1SGeorge Keishing "Members": [ 204dabf38f1SGeorge Keishing { 205dabf38f1SGeorge Keishing "@odata.id": "/redfish/v1/SessionService/Sessions/Z5HummWPZ7" 206dabf38f1SGeorge Keishing } 207dabf38f1SGeorge Keishing { 208dabf38f1SGeorge Keishing "@odata.id": "/redfish/v1/SessionService/Sessions/46CmQmEL7H" 209dabf38f1SGeorge Keishing } 210dabf38f1SGeorge Keishing ], 211dabf38f1SGeorge Keishing """ 212dabf38f1SGeorge Keishing 213dabf38f1SGeorge Keishing member_list = [] 214dabf38f1SGeorge Keishing resp_list_dict = self.get_attribute(resource_path, "Members") 215dabf38f1SGeorge Keishing if resp_list_dict is None: 216dabf38f1SGeorge Keishing return member_list 217dabf38f1SGeorge Keishing 218dabf38f1SGeorge Keishing for member_id in range(0, len(resp_list_dict)): 219dabf38f1SGeorge Keishing member_list.append(resp_list_dict[member_id]["@odata.id"]) 220dabf38f1SGeorge Keishing 221dabf38f1SGeorge Keishing return member_list 222dabf38f1SGeorge Keishing 223f2613b7aSGeorge Keishing def list_request(self, resource_path): 224f2613b7aSGeorge Keishing r""" 225f2613b7aSGeorge Keishing Perform a GET list request and return available resource paths. 226f2613b7aSGeorge Keishing Description of argument(s): 227f2613b7aSGeorge Keishing resource_path URI resource absolute path 228f2613b7aSGeorge Keishing (e.g. "/redfish/v1/SessionService/Sessions"). 229f2613b7aSGeorge Keishing """ 230c86a2f72SMichael Walsh gp.qprint_executing(style=gp.func_line_style_short) 231c86a2f72SMichael Walsh # Set quiet variable to keep subordinate get() calls quiet. 232c86a2f72SMichael Walsh quiet = 1 23362dfb864SAnusha Dathatri self.__pending_enumeration = set() 23420f38712SPatrick Williams self._rest_response_ = self._redfish_.get( 23520f38712SPatrick Williams resource_path, valid_status_codes=[200, 404, 500] 23620f38712SPatrick Williams ) 237f2613b7aSGeorge Keishing 238f2613b7aSGeorge Keishing # Return empty list. 239f2613b7aSGeorge Keishing if self._rest_response_.status != 200: 24062dfb864SAnusha Dathatri return self.__pending_enumeration 241f2613b7aSGeorge Keishing self.walk_nested_dict(self._rest_response_.dict) 24262dfb864SAnusha Dathatri if not self.__pending_enumeration: 24362dfb864SAnusha Dathatri return resource_path 24462dfb864SAnusha Dathatri for resource in self.__pending_enumeration.copy(): 24520f38712SPatrick Williams self._rest_response_ = self._redfish_.get( 24620f38712SPatrick Williams resource, valid_status_codes=[200, 404, 500] 24720f38712SPatrick Williams ) 24862dfb864SAnusha Dathatri 249f2613b7aSGeorge Keishing if self._rest_response_.status != 200: 250f2613b7aSGeorge Keishing continue 251f2613b7aSGeorge Keishing self.walk_nested_dict(self._rest_response_.dict) 25262dfb864SAnusha Dathatri return list(sorted(self.__pending_enumeration)) 253f2613b7aSGeorge Keishing 25420f38712SPatrick Williams def enumerate_request( 25520f38712SPatrick Williams self, resource_path, return_json=1, include_dead_resources=False 25620f38712SPatrick Williams ): 257f2613b7aSGeorge Keishing r""" 258f2613b7aSGeorge Keishing Perform a GET enumerate request and return available resource paths. 259f2613b7aSGeorge Keishing 260f2613b7aSGeorge Keishing Description of argument(s): 26137e028f3SMichael Walsh resource_path URI resource absolute path (e.g. 26237e028f3SMichael Walsh "/redfish/v1/SessionService/Sessions"). 26337e028f3SMichael Walsh return_json Indicates whether the result should be 26437e028f3SMichael Walsh returned as a json string or as a 26537e028f3SMichael Walsh dictionary. 2663e7930dfSAnusha Dathatri include_dead_resources Check and return a list of dead/broken URI 2673e7930dfSAnusha Dathatri resources. 268f2613b7aSGeorge Keishing """ 269f2613b7aSGeorge Keishing 270c86a2f72SMichael Walsh gp.qprint_executing(style=gp.func_line_style_short) 271c86a2f72SMichael Walsh 27237e028f3SMichael Walsh return_json = int(return_json) 27337e028f3SMichael Walsh 274c86a2f72SMichael Walsh # Set quiet variable to keep subordinate get() calls quiet. 275c86a2f72SMichael Walsh quiet = 1 276c86a2f72SMichael Walsh 27762dfb864SAnusha Dathatri # Variable to hold enumerated data. 27862dfb864SAnusha Dathatri self.__result = {} 279f2613b7aSGeorge Keishing 28062dfb864SAnusha Dathatri # Variable to hold the pending list of resources for which enumeration. 28162dfb864SAnusha Dathatri # is yet to be obtained. 28262dfb864SAnusha Dathatri self.__pending_enumeration = set() 283f2613b7aSGeorge Keishing 28462dfb864SAnusha Dathatri self.__pending_enumeration.add(resource_path) 285f2613b7aSGeorge Keishing 28662dfb864SAnusha Dathatri # Variable having resources for which enumeration is completed. 28762dfb864SAnusha Dathatri enumerated_resources = set() 28862dfb864SAnusha Dathatri 2893e7930dfSAnusha Dathatri if include_dead_resources: 2903e7930dfSAnusha Dathatri dead_resources = {} 2913e7930dfSAnusha Dathatri 29262dfb864SAnusha Dathatri resources_to_be_enumerated = (resource_path,) 29362dfb864SAnusha Dathatri 29462dfb864SAnusha Dathatri while resources_to_be_enumerated: 29562dfb864SAnusha Dathatri for resource in resources_to_be_enumerated: 2966d2d42fcSAnusha Dathatri # JsonSchemas, SessionService or URLs containing # are not 2976d2d42fcSAnusha Dathatri # required in enumeration. 29848156e71SGeorge Keishing # Example: '/redfish/v1/JsonSchemas/' and sub resources. 299cdb77dbaSAnusha Dathatri # '/redfish/v1/SessionService' 300*4d430283Sganesanb # '/redfish/v1/Managers/${MANAGER_ID}#/Oem' 30120f38712SPatrick Williams if ( 30220f38712SPatrick Williams ("JsonSchemas" in resource) 30320f38712SPatrick Williams or ("SessionService" in resource) 30420f38712SPatrick Williams or ("PostCodes" in resource) 30520f38712SPatrick Williams or ("Registries" in resource) 30620f38712SPatrick Williams or ("Journal" in resource) 30720f38712SPatrick Williams or ("#" in resource) 30820f38712SPatrick Williams ): 30948156e71SGeorge Keishing continue 31062dfb864SAnusha Dathatri 31120f38712SPatrick Williams self._rest_response_ = self._redfish_.get( 31220f38712SPatrick Williams resource, valid_status_codes=[200, 404, 405, 500] 31320f38712SPatrick Williams ) 31462dfb864SAnusha Dathatri # Enumeration is done for available resources ignoring the 31562dfb864SAnusha Dathatri # ones for which response is not obtained. 316f2613b7aSGeorge Keishing if self._rest_response_.status != 200: 3173e7930dfSAnusha Dathatri if include_dead_resources: 3183e7930dfSAnusha Dathatri try: 3193e7930dfSAnusha Dathatri dead_resources[self._rest_response_.status].append( 32020f38712SPatrick Williams resource 32120f38712SPatrick Williams ) 3223e7930dfSAnusha Dathatri except KeyError: 32320f38712SPatrick Williams dead_resources[self._rest_response_.status] = [ 32420f38712SPatrick Williams resource 32520f38712SPatrick Williams ] 326f2613b7aSGeorge Keishing continue 327f2613b7aSGeorge Keishing 32862dfb864SAnusha Dathatri self.walk_nested_dict(self._rest_response_.dict, url=resource) 32962dfb864SAnusha Dathatri 33062dfb864SAnusha Dathatri enumerated_resources.update(set(resources_to_be_enumerated)) 33120f38712SPatrick Williams resources_to_be_enumerated = tuple( 33220f38712SPatrick Williams self.__pending_enumeration - enumerated_resources 33320f38712SPatrick Williams ) 33462dfb864SAnusha Dathatri 33537e028f3SMichael Walsh if return_json: 3363e7930dfSAnusha Dathatri if include_dead_resources: 33720f38712SPatrick Williams return ( 33820f38712SPatrick Williams json.dumps( 33920f38712SPatrick Williams self.__result, 34020f38712SPatrick Williams sort_keys=True, 34120f38712SPatrick Williams indent=4, 34220f38712SPatrick Williams separators=(",", ": "), 34320f38712SPatrick Williams ), 34420f38712SPatrick Williams dead_resources, 34520f38712SPatrick Williams ) 3463e7930dfSAnusha Dathatri else: 34720f38712SPatrick Williams return json.dumps( 34820f38712SPatrick Williams self.__result, 34920f38712SPatrick Williams sort_keys=True, 35020f38712SPatrick Williams indent=4, 35120f38712SPatrick Williams separators=(",", ": "), 35220f38712SPatrick Williams ) 35337e028f3SMichael Walsh else: 3543e7930dfSAnusha Dathatri if include_dead_resources: 3553e7930dfSAnusha Dathatri return self.__result, dead_resources 3563e7930dfSAnusha Dathatri else: 35737e028f3SMichael Walsh return self.__result 358f2613b7aSGeorge Keishing 35920f38712SPatrick Williams def walk_nested_dict(self, data, url=""): 360f2613b7aSGeorge Keishing r""" 361f2613b7aSGeorge Keishing Parse through the nested dictionary and get the resource id paths. 362f2613b7aSGeorge Keishing Description of argument(s): 363f2613b7aSGeorge Keishing data Nested dictionary data from response message. 36462dfb864SAnusha Dathatri url Resource for which the response is obtained in data. 365f2613b7aSGeorge Keishing """ 36620f38712SPatrick Williams url = url.rstrip("/") 367f2613b7aSGeorge Keishing 368f2613b7aSGeorge Keishing for key, value in data.items(): 36962dfb864SAnusha Dathatri # Recursion if nested dictionary found. 370f2613b7aSGeorge Keishing if isinstance(value, dict): 371f2613b7aSGeorge Keishing self.walk_nested_dict(value) 372f2613b7aSGeorge Keishing else: 37362dfb864SAnusha Dathatri # Value contains a list of dictionaries having member data. 37420f38712SPatrick Williams if "Members" == key: 375f2613b7aSGeorge Keishing if isinstance(value, list): 37662dfb864SAnusha Dathatri for memberDict in value: 3770d305d3eSAnusha Dathatri if isinstance(memberDict, str): 3780d305d3eSAnusha Dathatri self.__pending_enumeration.add(memberDict) 3790d305d3eSAnusha Dathatri else: 38020f38712SPatrick Williams self.__pending_enumeration.add( 38120f38712SPatrick Williams memberDict["@odata.id"] 38220f38712SPatrick Williams ) 3830d305d3eSAnusha Dathatri 38420f38712SPatrick Williams if "@odata.id" == key: 38520f38712SPatrick Williams value = value.rstrip("/") 38662dfb864SAnusha Dathatri # Data for the given url. 38762dfb864SAnusha Dathatri if value == url: 38862dfb864SAnusha Dathatri self.__result[url] = data 38962dfb864SAnusha Dathatri # Data still needs to be looked up, 39062dfb864SAnusha Dathatri else: 39162dfb864SAnusha Dathatri self.__pending_enumeration.add(value) 3927ec45937SGeorge Keishing 3937ec45937SGeorge Keishing def get_key_value_nested_dict(self, data, key): 3947ec45937SGeorge Keishing r""" 3957ec45937SGeorge Keishing Parse through the nested dictionary and get the searched key value. 3967ec45937SGeorge Keishing 3977ec45937SGeorge Keishing Description of argument(s): 3987ec45937SGeorge Keishing data Nested dictionary data from response message. 3997ec45937SGeorge Keishing key Search dictionary key element. 4007ec45937SGeorge Keishing """ 4017ec45937SGeorge Keishing 4027ec45937SGeorge Keishing for k, v in data.items(): 4037ec45937SGeorge Keishing if isinstance(v, dict): 4047ec45937SGeorge Keishing self.get_key_value_nested_dict(v, key) 4057ec45937SGeorge Keishing 4067ec45937SGeorge Keishing if k == key: 4077ec45937SGeorge Keishing target_list.append(v) 408