+ Collections.Sort_List ${candidate_list}
+ [Return] ${owner} ${candidate_list}
+
+Get_Owner_And_Candidates_For_Device_Singleton
+ [Arguments] ${device_name} ${device_type} ${member_index} ${http_timeout}=${EMPTY}
+ [Documentation] Returns the owner and a list of candidates for the SB device ${device_name} of type ${device_type}. Request is sent to member ${member_index}.
+ ... Parsing method is selected by device type
+ ... Separate kw for every supported device type must be defined
+ BuiltIn.Keyword_Should_Exist Get_Owner_And_Candidates_For_Device_Singleton_${device_type}
+ BuiltIn.Run_Keyword_And_Return Get_Owner_And_Candidates_For_Device_Singleton_${device_type} ${device_name} ${member_index} http_timeout=${http_timeout}
+
+Get_Owner_And_Candidates_For_Device_Singleton_Netconf
+ [Arguments] ${device_name} ${member_index} ${http_timeout}=${EMPTY}
+ [Documentation] Returns the owner and a list of candidates for the SB device ${device_name} of type netconf. Request is sent to member ${member_index}.
+ ... Parsing method is set as netconf (using netconf device id prefix and suffix)
+ # Get election entity type results
+ ${type} = BuiltIn.Set_Variable ${SINGLETON_ELECTION_ENTITY_TYPE}
+ ${id} = CompareStream.Set_Variable_If_At_Least_Fluorine ${SINGLETON_NETCONF_DEVICE_ID_PREFIX}${device_name}${SINGLETON_NETCONF_DEVICE_ID_SUFFIX} ${SINGLETON_NETCONF_DEVICE_ID_PREFIX_OLD}${device_name}${SINGLETON_NETCONF_DEVICE_ID_SUFFIX_OLD}
+ ${owner_1} ${candidate_list_1} = Get_Owner_And_Candidates_For_Type_And_Id ${type} ${id} ${member_index} http_timeout=${http_timeout}
+ # Get change ownership entity type results
+ ${type} = BuiltIn.Set_Variable ${SINGLETON_CHANGE_OWNERSHIP_ENTITY_TYPE}
+ ${id} = CompareStream.Set_Variable_If_At_Least_Fluorine ${SINGLETON_NETCONF_DEVICE_ID_PREFIX}${device_name}${SINGLETON_NETCONF_DEVICE_ID_SUFFIX} ${SINGLETON_NETCONF_DEVICE_ID_PREFIX_OLD}${device_name}${SINGLETON_NETCONF_DEVICE_ID_SUFFIX_OLD}
+ ${owner_2} ${candidate_list_2} = Get_Owner_And_Candidates_For_Type_And_Id ${type} ${id} ${member_index} http_timeout=${http_timeout}
+ # Owners must be same, if not, there is still some election or change ownership in progress
+ BuiltIn.Should_Be_Equal_As_Integers ${owner_1} ${owner_2} Owners for device ${device_name} are not same
+ [Return] ${owner_1} ${candidate_list_1}
+
+Get_Owner_And_Candidates_For_Device_Singleton_Bgpcep
+ [Arguments] ${device_name} ${member_index} ${http_timeout}=${EMPTY}
+ [Documentation] Returns the owner and a list of candidates for the SB device ${device_name}. Request is sent to member ${member_index}.
+ # Get election entity type results
+ ${type} = BuiltIn.Set_Variable ${SINGLETON_ELECTION_ENTITY_TYPE}
+ ${id} = BuiltIn.Set_Variable ${SINGLETON_BGPCEP_DEVICE_ID_PREFIX}${device_name}${SINGLETON_BGPCEP_DEVICE_ID_SUFFIX}
+ ${owner_1} ${candidate_list_1} = Get_Owner_And_Candidates_For_Type_And_Id ${type} ${id} ${member_index} http_timeout=${http_timeout}
+ # Get change ownership entity type results
+ ${type} = BuiltIn.Set_Variable ${SINGLETON_CHANGE_OWNERSHIP_ENTITY_TYPE}
+ ${id} = BuiltIn.Set_Variable ${SINGLETON_BGPCEP_DEVICE_ID_PREFIX}${device_name}${SINGLETON_BGPCEP_DEVICE_ID_SUFFIX}
+ ${owner_2} ${candidate_list_2} = Get_Owner_And_Candidates_For_Type_And_Id ${type} ${id} ${member_index} http_timeout=${http_timeout}
+ # Owners must be same, if not, there is still some election or change ownership in progress
+ BuiltIn.Should_Be_Equal_As_Integers ${owner_1} ${owner_2} Owners for device ${device_name} are not same
+ [Return] ${owner_1} ${candidate_list_1}
+
+Get_Owner_And_Candidates_For_Device_Singleton_Sxp
+ [Arguments] ${device_name} ${member_index} ${http_timeout}=${EMPTY}
+ [Documentation] Returns the owner and a list of candidates for the SB device ${device_name}. Request is sent to member ${member_index}.
+ # Get election entity type results
+ ${type} = BuiltIn.Set_Variable ${SINGLETON_ELECTION_ENTITY_TYPE}
+ ${id} = BuiltIn.Set_Variable ${SINGLETON_SXP_DEVICE_ID_PREFIX}${device_name}${SINGLETON_SXP_DEVICE_ID_SUFFIX}
+ ${owner_1} ${candidate_list_1} = Get_Owner_And_Candidates_For_Type_And_Id ${type} ${id} ${member_index} http_timeout=${http_timeout}
+ # Get change ownership entity type results
+ ${type} = BuiltIn.Set_Variable ${SINGLETON_CHANGE_OWNERSHIP_ENTITY_TYPE}
+ ${id} = BuiltIn.Set_Variable ${SINGLETON_SXP_DEVICE_ID_PREFIX}${device_name}${SINGLETON_SXP_DEVICE_ID_SUFFIX}
+ ${owner_2} ${candidate_list_2} = Get_Owner_And_Candidates_For_Type_And_Id ${type} ${id} ${member_index} http_timeout=${http_timeout}
+ # Owners must be same, if not, there is still some election or change ownership in progress
+ BuiltIn.Should_Be_Equal_As_Integers ${owner_1} ${owner_2} Owners for device ${device_name} are not same
+ [Return] ${owner_1} ${candidate_list_1}
+
+Get_Owner_And_Candidates_For_Device
+ [Arguments] ${device_name} ${device_type} ${member_index} ${http_timeout}=${EMPTY}
+ [Documentation] Returns the owner and a list of candidates for the SB device ${device_name} of type ${device_type}. Request is sent to member ${member_index}.
+ ... If parsing as singleton failed, kw try to parse data in old way (without singleton).
+ ... Candidates are all members that register to own a device, so the list of candiates includes the owner.
+ ... The returned candidate list is sorted numerically.
+ ... Note that "candidate list" definition currently differs between Beryllium and Boron.
+ ... It is recommended to use Get_Owner_And_Successors_For_Device instead of this keyword, see documentation there.
+ # Try singleton
+ ${status} ${results} = BuiltIn.Run_Keyword_And_Ignore_Error Get_Owner_And_Candidates_For_Device_Singleton device_name=${device_name} device_type=${device_type} member_index=${member_index}
+ ... http_timeout=${http_timeout}
+ BuiltIn.Return_From_Keyword_If "${status}"=="PASS" ${results}
+ # If singleton failed, try parsing in old way
+ ${status} ${results} = BuiltIn.Run_Keyword_And_Ignore_Error Get_Owner_And_Candidates_For_Device_Old device_name=${device_name} device_type=${device_type} member_index=${member_index}
+ ... http_timeout=${http_timeout}
+ # previous 3 lines (BuilIn.Return.., # If singleton..., ${status}....) could be deleted when old way will not be supported anymore
+ BuiltIn.Run_Keyword_If '${status}'=='FAIL' BuiltIn.Fail Could not parse owner and candidates for device ${device_name}
+ [Return] @{results}
+
+Check_Old_Owner_Stays_Elected_For_Device
+ [Arguments] ${device_name} ${device_type} ${old_owner} ${node_to_ask} ${http_timeout}=${EMPTY}
+ [Documentation] Verify the owner remain the same as ${old_owner}
+ ${owner} ${candidates} = Get_Owner_And_Candidates_For_Device ${device_name} ${device_type} ${node_to_ask} http_timeout=${http_timeout}
+ BuiltIn.Should_Be_Equal_As_numbers ${old_owner} ${owner}
+ BuiltIn.Return_From_Keyword ${owner} ${candidates}
+
+Check_New_Owner_Got_Elected_For_Device
+ [Arguments] ${device_name} ${device_type} ${old_owner} ${node_to_ask} ${http_timeout}=${EMPTY}
+ [Documentation] Verify new owner was elected comparing to ${old_owner}
+ ${owner} ${candidates} = Get_Owner_And_Candidates_For_Device ${device_name} ${device_type} ${node_to_ask} http_timeout=${http_timeout}
+ BuiltIn.Should_Not_Be_Equal_As_Numbers ${old_owner} ${owner}
+ BuiltIn.Return_From_Keyword ${owner} ${candidates}
+
+Get_Owner_And_Candidates_For_Type_And_Id
+ [Arguments] ${type} ${id} ${member_index} ${require_candidate_list}=${EMPTY} ${http_timeout}=${EMPTY}
+ [Documentation] Returns the owner and a list of candidates for entity specified by ${type} and ${id}
+ ... Request is sent to member ${member_index}.
+ ... Candidates are all members that register to own a device, so the list of candiates includes the owner.
+ ... Bear in mind that for Boron and beyond, candidates are not removed on node down or isolation.
+ ... If ${require_candidate_list} is not \${EMPTY}, check whether the actual list of candidates matches.
+ ... Note that differs from "given list" semantics used in other keywords,
+ ... namely you cannot use \${EMPTY} to stand for "full list" in this keyword.
+ BuiltIn.Comment TODO: Find a way to unify and deduplicate code blocks in Get_Owner_And_Candidates_* keywords.
+ ${session} = Resolve_Http_Session_For_Member member_index=${member_index}
+ ${data} = TemplatedRequests.Get_As_Json_From_Uri uri=${ENTITY_OWNER_URI} session=${session} http_timeout=${http_timeout}
+ ${candidate_list} = BuiltIn.Create_List
+ ${json} = RequestsLibrary.To_Json ${data}
+ ${entity_type_list} = Collections.Get_From_Dictionary &{json}[entity-owners] entity-type
+ ${entity_type_index} = Utils.Get_Index_From_List_Of_Dictionaries ${entity_type_list} type ${type}
+ BuiltIn.Should_Not_Be_Equal_As_Integers ${entity_type_index} -1 No Entity Owner found for ${type}
+ ${entity_list} = Collections.Get_From_Dictionary @{entity_type_list}[${entity_type_index}] entity
+ ${entity_index} = Utils.Get_Index_From_List_Of_Dictionaries ${entity_list} id ${id}
+ BuiltIn.Should Not_Be_Equal_As_Integers ${entity_index} -1 Id ${id} not found in Entity Owner ${type}
+ ${entity_owner} = Collections.Get_From_Dictionary @{entity_list}[${entity_index}] owner
+ BuiltIn.Should_Not_Be_Empty ${entity_owner} No owner found for type=${type} id=${id}
+ ${owner} = String.Replace_String ${entity_owner} member- ${EMPTY}
+ ${owner} = BuiltIn.Convert_To_Integer ${owner}
+ ${entity_candidates_list} = Collections.Get_From_Dictionary @{entity_list}[${entity_index}] candidate
+ : FOR ${entity_candidate} IN @{entity_candidates_list}
+ \ ${candidate} = String.Replace_String &{entity_candidate}[name] member- ${EMPTY}
+ \ ${candidate} = BuiltIn.Convert_To_Integer ${candidate}
+ \ Collections.Append_To_List ${candidate_list} ${candidate}
+ Collections.Sort_List ${candidate_list}
+ BuiltIn.Comment TODO: Separate check lines into Verify_Owner_And_Candidates_For_Type_And_Id
+ BuiltIn.Run_Keyword_If """${require_candidate_list}""" BuiltIn.Should_Be_Equal ${require_candidate_list} ${candidate_list} Candidate list does not match: ${candidate_list} is not ${require_candidate_list}