... perhaps explicit ${ACCEPT_JSON} will be better, even if it sends few bytes more?
Library Collections
Library OperatingSystem
+Library String
Library RequestsLibrary
Library ${CURDIR}/norm_json.py
-Variables ${CURDIR}/../variables/Variables.py
+Resource ${CURDIR}/../variables/Variables.robot
*** Variables ***
# TODO: Make the following list more narrow when streams without Bug 2594 fix (up to beryllium) are no longer used.
@{ALLOWED_DELETE_STATUS_CODES} ${200} ${201} ${204} ${404} # List of integers, not strings. Used by DELETE if the resource may be not present.
@{ALLOWED_STATUS_CODES} ${200} ${201} ${204} # List of integers, not strings. Used by both PUT and DELETE (if the resource should have been present).
@{DATA_VALIDATION_ERROR} ${400} # For testing mildly negative scenarios where ODL reports user error.
-@{DELETED_STATUS_CODE} ${404} # List of integers, not strings. Used by DELETE if the resource may be not present.
+@{DELETED_STATUS_CODES} ${404} ${409} # List of integers, not strings. Used by DELETE if the resource may be not present.
# TODO: Add option for delete to require 404.
@{INTERNAL_SERVER_ERROR} ${500} # Only for testing severely negative scenarios where ODL cannot recover.
@{KEYS_WITH_BITS} op # the default list with keys to be sorted when norm_json libray is used
*** Keywords ***
Create_Default_Session
- [Arguments] ${url}=http://${ODL_SYSTEM_IP}:${RESTCONFPORT} ${auth}=${AUTH} ${timeout}=1 ${max_retries}=0
+ [Arguments] ${url}=http://${ODL_SYSTEM_IP}:${RESTCONFPORT} ${auth}=${AUTH} ${timeout}=${DEFAULT_TIMEOUT_HTTP} ${max_retries}=0
[Documentation] Create "default" session to ${url} with authentication and connection parameters.
... This Keyword is in this Resource only so that user do not need to call RequestsLibrary directly.
RequestsLibrary.Create_Session alias=default url=${url} auth=${auth} timeout=${timeout} max_retries=${max_retries}
Get_As_Json_Templated
[Arguments] ${folder} ${mapping}={} ${session}=default ${verify}=False ${iterations}=${EMPTY} ${iter_start}=1
- ... ${http_timeout}=${EMPTY}
+ ... ${http_timeout}=${EMPTY} ${log_response}=True
[Documentation] Add arguments sensible for JSON data, return Get_Templated response text.
... Optionally, verification against JSON data (may be iterated) is called.
... Only subset of JSON data is verified and returned if JMES path is specified in
... file ${folder}${/}jmespath.expr.
${response_text} = Get_Templated folder=${folder} mapping=${mapping} accept=${ACCEPT_EMPTY} session=${session} normalize_json=True
- ... http_timeout=${http_timeout}
+ ... http_timeout=${http_timeout} log_response=${log_response}
BuiltIn.Run_Keyword_If ${verify} Verify_Response_As_Json_Templated response=${response_text} folder=${folder} base_name=data mapping=${mapping}
... iterations=${iterations} iter_start=${iter_start}
[Return] ${response_text}
BuiltIn.Run_Keyword_If ${verify} Verify_Response_As_Json_Templated response=${response_text} folder=${folder} base_name=response mapping=${mapping}
[Return] ${response_text}
+Post_As_Json_Rfc8040_Templated
+ [Arguments] ${folder} ${mapping}={} ${session}=default ${verify}=False ${iterations}=${EMPTY} ${iter_start}=1
+ ... ${additional_allowed_status_codes}=${NO_STATUS_CODES} ${explicit_status_codes}=${NO_STATUS_CODES} ${http_timeout}=${EMPTY}
+ [Documentation] Add arguments sensible for JSON data, return Post_Templated response text.
+ ... Optionally, verification against response.json (no iteration) is called.
+ ... Only subset of JSON data is verified and returned if JMES path is specified in
+ ... file ${folder}${/}jmespath.expr.
+ ... Response status code must be one of values from ${explicit_status_codes} if specified or one of set
+ ... created from all positive HTTP status codes together with ${additional_allowed_status_codes}.
+ ... RFC8040 defines RESTCONF protocol, for configuring data defined in YANG version 1
+ ... or YANG version 1.1, using the datastore concepts defined in NETCONF.
+ ${response_text} = Post_Templated folder=${folder} base_name=data extension=json accept=${ACCEPT_EMPTY} content_type=${HEADERS_YANG_RFC8040_JSON}
+ ... mapping=${mapping} session=${session} normalize_json=True endline=${\n} iterations=${iterations} iter_start=${iter_start}
+ ... additional_allowed_status_codes=${additional_allowed_status_codes} explicit_status_codes=${explicit_status_codes} http_timeout=${http_timeout}
+ BuiltIn.Run_Keyword_If ${verify} Verify_Response_As_Json_Templated response=${response_text} folder=${folder} base_name=response mapping=${mapping}
+ [Return] ${response_text}
+
Post_As_Xml_Templated
[Arguments] ${folder} ${mapping}={} ${session}=default ${verify}=False ${iterations}=${EMPTY} ${iter_start}=1
... ${additional_allowed_status_codes}=${NO_STATUS_CODES} ${explicit_status_codes}=${NO_STATUS_CODES} ${http_timeout}=${EMPTY}
[Return] ${response_text}
Delete_Templated
- [Arguments] ${folder} ${mapping}={} ${session}=default ${additional_allowed_status_codes}=${NO_STATUS_CODES} ${http_timeout}=${EMPTY}
+ [Arguments] ${folder} ${mapping}={} ${session}=default ${additional_allowed_status_codes}=${NO_STATUS_CODES} ${http_timeout}=${EMPTY} ${location}=location
[Documentation] Resolve URI from folder, issue DELETE request.
- ${uri} = Resolve_Text_From_Template_Folder folder=${folder} base_name=location extension=uri mapping=${mapping}
+ ${uri} = Resolve_Text_From_Template_Folder folder=${folder} base_name=${location} extension=uri mapping=${mapping}
${response_text} = Delete_From_Uri uri=${uri} session=${session} additional_allowed_status_codes=${additional_allowed_status_codes} http_timeout=${http_timeout}
[Return] ${response_text}
... endline=${EMPTY} iterations=${iterations} iter_start=${iter_start}
Get_As_Json_From_Uri
- [Arguments] ${uri} ${session}=default ${http_timeout}=${EMPTY}
+ [Arguments] ${uri} ${session}=default ${http_timeout}=${EMPTY} ${log_response}=True
[Documentation] Specify JSON headers and return Get_From_Uri normalized response text.
- ${response_text} = Get_From_Uri uri=${uri} accept=${ACCEPT_EMPTY} session=${session} normalize_json=True http_timeout=${http_timeout}
+ ${response_text} = Get_From_Uri uri=${uri} accept=${ACCEPT_EMPTY} session=${session} normalize_json=True http_timeout=${http_timeout} log_response=${log_response}
[Return] ${response_text}
Get_As_Xml_From_Uri
- [Arguments] ${uri} ${session}=default ${http_timeout}=${EMPTY}
+ [Arguments] ${uri} ${session}=default ${http_timeout}=${EMPTY} ${log_response}=True
[Documentation] Specify XML headers and return Get_From_Uri response text.
- ${response_text} = Get_From_Uri uri=${uri} accept=${ACCEPT_XML} session=${session} normalize_json=False http_timeout=${http_timeout}
+ ${response_text} = Get_From_Uri uri=${uri} accept=${ACCEPT_XML} session=${session} normalize_json=False http_timeout=${http_timeout} log_response=${log_response}
[Return] ${response_text}
Put_As_Json_To_Uri
${expression} = BuiltIn.Set Variable If ${read_jmes_file} == ${true} ${jmes_expression} ${EMPTY}
[Return] ${expression}
+Resolve_Volatiles_Path
+ [Arguments] ${folder}
+ [Documentation] Reads Volatiles List from file ${folder}${/}volatiles.list if the file exists and
+ ... returns the Volatiles List. Empty string is returned otherwise.
+ ${read_volatiles_file} = BuiltIn.Run Keyword And Return Status OperatingSystem.File Should Exist ${folder}${/}volatiles.list
+ Return From Keyword If ${read_volatiles_file} == ${false} ${EMPTY}
+ ${volatiles}= OperatingSystem.Get_File ${folder}${/}volatiles.list
+ ${volatiles_list}= String.Split_String ${volatiles} ${\n}
+ [Return] ${volatiles_list}
+
Get_Templated
- [Arguments] ${folder} ${accept} ${mapping}={} ${session}=default ${normalize_json}=False ${http_timeout}=${EMPTY}
+ [Arguments] ${folder} ${accept} ${mapping}={} ${session}=default ${normalize_json}=False ${http_timeout}=${EMPTY} ${log_response}=True
[Documentation] Resolve URI from folder, call Get_From_Uri, return response text.
${uri} = Resolve_Text_From_Template_Folder folder=${folder} base_name=location extension=uri mapping=${mapping}
${jmes_expression} = Resolve_Jmes_Path ${folder}
+ ${volatiles_list}= Resolve_Volatiles_Path ${folder}
${response_text} = Get_From_Uri uri=${uri} accept=${accept} session=${session} normalize_json=${normalize_json} jmes_path=${jmes_expression}
- ... http_timeout=${http_timeout}
+ ... http_timeout=${http_timeout} keys_with_volatiles=${volatiles_list} log_response=${log_response}
[Return] ${response_text}
Put_Templated
Get_From_Uri
[Arguments] ${uri} ${accept}=${ACCEPT_EMPTY} ${session}=default ${normalize_json}=False ${jmes_path}=${EMPTY} ${http_timeout}=${EMPTY}
+ ... ${keys_with_volatiles}=${EMPTY} ${log_response}=True
[Documentation] GET data from given URI, check status code and return response text.
... \${accept} is a Python object with headers to use.
... If \${normalize_json}, normalize as JSON text before returning.
BuiltIn.Log ${accept}
${response} = BuiltIn.Run_Keyword_If """${http_timeout}""" == """${EMPTY}""" RequestsLibrary.Get_Request alias=${session} uri=${uri} headers=${accept}
... ELSE RequestsLibrary.Get_Request alias=${session} uri=${uri} headers=${accept} timeout=${http_timeout}
- Check_Status_Code ${response}
+ Check_Status_Code ${response} log_response=${log_response}
BuiltIn.Run_Keyword_Unless ${normalize_json} BuiltIn.Return_From_Keyword ${response.text}
- ${text_normalized} = norm_json.normalize_json_text ${response.text} jmes_path=${jmes_path}
+ ${text_normalized} = norm_json.normalize_json_text ${response.text} jmes_path=${jmes_path} keys_with_volatiles=${keys_with_volatiles}
[Return] ${text_normalized}
Put_To_Uri
[Return] ${text_normalized}
Check_Status_Code
- [Arguments] ${response} ${additional_allowed_status_codes}=${NO_STATUS_CODES} ${explicit_status_codes}=${NO_STATUS_CODES}
- [Documentation] Log response text, check status_code is one of allowed ones.
+ [Arguments] ${response} ${additional_allowed_status_codes}=${NO_STATUS_CODES} ${explicit_status_codes}=${NO_STATUS_CODES} ${log_response}=True
+ [Documentation] Log response text, check status_code is one of allowed ones. In cases where this keyword is
+ ... called in a WUKS it could end up logging tons of data and it may be desired to skip the logging by passing
+ ... log_response=False, but by default it remains True.
# TODO: Remove overlap with keywords from Utils.robot
- BuiltIn.Log ${response.text}
- BuiltIn.Log ${response.status_code}
- BuiltIn.Run_Keyword_And_Return_If """${explicit_status_codes}""" != """${NO_STATUS_CODES}""" Collections.List_Should_Contain_Value ${explicit_status_codes} ${response.status_code}
- ${final_allowd_list} = Collections.Combine_Lists ${ALLOWED_STATUS_CODES} ${additional_allowed_status_codes}
+ Run Keyword If "${log_response}" == "True" BuiltIn.Log ${response.text}
+ Run Keyword If "${log_response}" == "True" BuiltIn.Log ${response.status_code}
+ # In order to allow other existing keywords to consume this keyword by passing a single non-list status code, we need to
+ # check the type of the argument passed and convert those single non-list codes in to a one item list
+ ${status_codes_type} = Evaluate type($additional_allowed_status_codes).__name__
+ ${allowed_status_codes_list} = Run Keyword If "${status_codes_type}"!="list" Create List ${additional_allowed_status_codes}
+ ... ELSE Set Variable ${additional_allowed_status_codes}
+ ${status_codes_type} = Evaluate type($explicit_status_codes).__name__
+ ${explicit_status_codes_list} = Run Keyword If "${status_codes_type}"!="list" Create List ${explicit_status_codes}
+ ... ELSE Set Variable ${explicit_status_codes}
+ BuiltIn.Run_Keyword_And_Return_If """${explicit_status_codes_list}""" != """${NO_STATUS_CODES}""" Collections.List_Should_Contain_Value ${explicit_status_codes_list} ${response.status_code}
+ ${final_allowd_list} = Collections.Combine_Lists ${ALLOWED_STATUS_CODES} ${allowed_status_codes_list}
Collections.List_Should_Contain_Value ${final_allowd_list} ${response.status_code}
Join_Two_Headers
${item_template} = Resolve_Text_From_Template_File folder=${folder} file_name=${base_name}.item.${extension} mapping=${mapping}
${items} = BuiltIn.Create_List
${separator} = BuiltIn.Set_Variable_If '${extension}' != 'json' ${endline} ,${endline}
- : FOR ${iteration} IN RANGE ${iter_start} ${iterations}+${iter_start}
- \ BuiltIn.Run_Keyword_If ${iteration} > ${iter_start} Collections.Append_To_List ${items} ${separator}
- \ ${item} = BuiltIn.Evaluate string.Template('''${item_template}''').substitute({"i":"${iteration}"}) modules=string
- \ Collections.Append_To_List ${items} ${item}
- # TODO: The following makes ugly result for iterations=0. Should we fix that?
+ FOR ${iteration} IN RANGE ${iter_start} ${iterations}+${iter_start}
+ BuiltIn.Run_Keyword_If ${iteration} > ${iter_start} Collections.Append_To_List ${items} ${separator}
+ ${item} = BuiltIn.Evaluate string.Template('''${item_template}''').substitute({"i":"${iteration}"}) modules=string
+ Collections.Append_To_List ${items} ${item}
+ # TODO: The following makes ugly result for iterations=0. Should we fix that?
+ END
${final_text} = BuiltIn.Catenate SEPARATOR= ${prolog} ${endline} @{items} ${endline}
... ${epilog}
[Return] ${final_text}