2 Documentation Robot keyword library (Resource) for runtime changes to config subsystem state using restconf calls.
4 ... Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
6 ... This program and the accompanying materials are made available under the
7 ... terms of the Eclipse Public License v1.0 which accompanies this distribution,
8 ... and is available at http://www.eclipse.org/legal/epl-v10.html
11 ... The purpose of this library is to make runtime changes to ODL configuration
12 ... easier from Robot suite contributor point of view.
14 ... Different ODL parts have varying ways of configuration,
15 ... this library affects only the Config Subsystem way.
16 ... Config Subsystem has (apart Java APIs mostly available only from inside of ODL)
17 ... NETCONF server as its publicly available entry point.
18 ... Netconf-connector feature makes this netconf server available for RESTCONF calls.
19 ... Unfortunately, URIs and data payloads tend to be quite convoluted,
20 ... so using RequestsLibrary directly from test cases is unwieldy.
22 ... The main strength of this library are *_Template_Folder_Config_Via_Restconf keywords
23 ... User gives a path to directory where files with templates for URI fragment
24 ... and XML (or JSON) data are present, and a mapping with substitution to make;
25 ... the keywords will take it from there.
28 ... * netconf-connector feature installed on ODL.
29 ... * Setup_Config_Via_Restconf called from suite Setup
30 ... (or before any other call to a keyword from this library) at least once.
31 Library OperatingSystem
32 Library RequestsLibrary
34 Library ${CURDIR}/HsfJson/hsf_json.py
35 Variables ${CURDIR}/../variables/Variables.py
38 # TODO: Make the following list more narrow when Bug 2594 is fixed.
39 @{allowed_status_codes} ${200} ${201} ${204} # List of integers, not strings. Used by both PUT and DELETE.
43 Setup_Config_Via_Restconf
44 [Documentation] Creates Requests session to be used by subsequent keywords.
45 ... Also remembers worspace to use when needed and two temp files for JSON data.
46 # Check for multiple Setup calls.
47 ${variable_was_set}= BuiltIn.Get_Variable_Value ${cvr_actfile} NEVER
48 BuiltIn.Return_From_Keyword_If '''${variable_was_set}''' != '''NEVER'''
49 # Do not append slash at the end uf URL, Requests would add another, resulting in error.
50 RequestsLibrary.Create_Session cvr_session http://${ODL_SYSTEM_IP}:${RESTCONFPORT}${CONTROLLER_CONFIG_MOUNT} headers=${HEADERS_XML} auth=${AUTH}
51 ${workspace_defined}= BuiltIn.Run_Keyword_And_return_Status BuiltIn.Variable_Should_Exist ${WORKSPACE}
52 BuiltIn.Run_Keyword_If ${workspace_defined} BuiltIn.Set_Suite_Variable ${cvr_workspace} ${WORKSPACE}
53 BuiltIn.Set_Suite_Variable ${cvr_actfile} ${cvr_workspace}${/}actual.json
54 BuiltIn.Set_Suite_Variable ${cvr_expfile} ${cvr_workspace}${/}expected.json
56 Teardown_Config_Via_Restconf
57 [Documentation] Teardown to pair with Setup (otherwise no-op).
58 BuiltIn.Comment TODO: The following line does not seem to be implemented by RequestsLibrary. Look for a workaround.
59 BuiltIn.Comment Delete_Session cvr_session
61 Put_Xml_Template_Folder_Config_Via_Restconf
62 [Arguments] ${folder} ${mapping_as_string}={}
63 [Documentation] Resolve URI and data from folder, PUT to controller config.
64 ${uri_part}= Resolve_URI_From_Template_Folder ${folder} ${mapping_as_string}
65 ${xml_data}= Resolve_Xml_Data_From_Template_Folder ${folder} ${mapping_as_string}
66 Put_Xml_Config_Via_Restconf ${uri_part} ${xml_data}
68 Get_Xml_Template_Folder_Config_Via_Restconf
69 [Arguments] ${folder} ${mapping_as_string}={}
70 [Documentation] Resolve URI from folder, GET from controller config in XML form.
71 ${uri_part}= Resolve_URI_From_Template_Folder ${folder} ${mapping_as_string}
72 ${xml_data}= Get_Xml_Config_Via_Restconf ${uri_part}
75 Get_Json_Template_Folder_Config_Via_Restconf
76 [Arguments] ${folder} ${mapping_as_string}={}
77 [Documentation] Resolve URI from folder, GET from controller config in JSON form.
78 ${uri_part}= Resolve_URI_From_Template_Folder ${folder} ${mapping_as_string}
79 ${json_string}= Get_Json_Config_Via_Restconf ${uri_part}
80 [Return] ${json_string}
82 Verify_Xml_Template_Folder_Config_Via_Restconf
83 [Arguments] ${folder} ${mapping_as_string}={}
84 [Documentation] Resolve URI from folder, GET from controller config, compare to expected data.
85 # FIXME: This is subject to pseudorandom field ordering issue, use with care.
86 ${expected_data}= Resolve_Xml_Data_From_Template_Folder ${folder} ${mapping_as_string}
87 ${actual_data}= Get_Xml_Template_Folder_Config_Via_Restconf ${folder} ${mapping_as_string}
88 BuiltIn.Should_Be_Equal ${actual_data} ${expected_data}
90 Verify_Json_Template_Folder_Config_Via_Restconf
91 [Arguments] ${folder} ${mapping_as_string}={}
92 [Documentation] Resolve URI from folder, GET from controller config, compare to expected data as normalized JSONs.
93 ${expected}= Resolve_Json_Data_From_Template_Folder ${folder} ${mapping_as_string}
94 ${actual}= Get_Json_Template_Folder_Config_Via_Restconf ${folder} ${mapping_as_string}
95 Normalize_Jsons_And_Compare ${actual} ${expected}
97 Normalize_Jsons_And_Compare
98 [Arguments] ${actual_raw} ${expected_raw}
99 [Documentation] Use HsfJson to normalize both arguments, compute and Log diff, fail if diff is non-empty.
100 ... This keywords assumes ${WORKSPACE} is defined as a suite variable.
101 ${actual_normalized}= hsf_json.Hsf_Json ${actual_raw}
102 ${expected_normalized}= hsf_json.Hsf_Json ${expected_raw}
103 OperatingSystem.Create_File ${cvr_expfile} ${expected_normalized}
104 OperatingSystem.Create_File ${cvr_actfile} ${actual_normalized}
105 ${diff}= OperatingSystem.Run diff -du '${cvr_expfile}' '${cvr_actfile}'
107 BuiltIn.Should_Be_Empty ${diff}
109 Delete_Xml_Template_Folder_Config_Via_Restconf
110 [Arguments] ${folder} ${mapping_as_string}={}
111 [Documentation] Resolve URI from folder, DELETE from controller config.
112 ${uri_part}= Resolve_URI_From_Template_Folder ${folder} ${mapping_as_string}
113 Delete_Config_Via_Restconf ${uri_part}
115 Resolve_URI_From_Template_Folder
116 [Arguments] ${folder} ${mapping_as_string}
117 [Documentation] Read URI template from folder, strip endline, make changes according to mapping, return the result.
118 ${status} ${uri_template}= BuiltIn.Run_Keyword_And_Ignore_Error OperatingSystem.Get_File ${folder}${/}config.uri.${ODL_STREAM}
119 ${uri_template}= BuiltIn.Run Keyword If '${status}' != 'PASS' OperatingSystem.Get_File ${folder}${/}config.uri
120 ... ELSE BuiltIn.Set Variable ${uri_template}
121 BuiltIn.Log ${uri_template}
122 ${uri_part}= Strip_Endline_And_Apply_Substitutions_From_Mapping ${uri_template} ${mapping_as_string}
125 Resolve_Xml_Data_From_Template_Folder
126 [Arguments] ${folder} ${mapping_as_string}
127 [Documentation] Read XML data template from folder, strip endline, make changes according to mapping, return the result.
128 ${status} ${data_template}= BuiltIn.Run_Keyword_And_Ignore_Error OperatingSystem.Get_File ${folder}${/}data.xml.${ODL_STREAM}
129 ${data_template}= BuiltIn.Run Keyword If '${status}' != 'PASS' OperatingSystem.Get_File ${folder}${/}data.xml
130 ... ELSE BuiltIn.Set Variable ${data_template}
131 BuiltIn.Log ${data_template}
132 ${xml_data}= Strip_Endline_And_Apply_Substitutions_From_Mapping ${data_template} ${mapping_as_string}
135 Resolve_Json_Data_From_Template_Folder
136 [Arguments] ${folder} ${mapping_as_string}
137 [Documentation] Read JSON data template from folder, strip endline, make changes according to mapping, return the result.
138 ${status} ${data_template}= BuiltIn.Run_Keyword_And_Ignore_Error OperatingSystem.Get_File ${folder}${/}data.json.${ODL_STREAM}
139 ${data_template}= BuiltIn.Run Keyword If '${status}' != 'PASS' OperatingSystem.Get_File ${folder}${/}data.json
140 ... ELSE BuiltIn.Set Variable ${data_template}
141 BuiltIn.Log ${data_template}
142 ${json_data}= Strip_Endline_And_Apply_Substitutions_From_Mapping ${data_template} ${mapping_as_string}
143 [Return] ${json_data}
145 Strip_Endline_And_Apply_Substitutions_From_Mapping
146 [Arguments] ${template_as_string} ${mapping_as_string}
147 [Documentation] Strip endline, apply substitutions, Log and return the result.
148 # Robot Framework does not understand dictionaries well, so resort to Evaluate.
149 # Needs python module "string", and since the template string is expected to contain newline, it has to be enclosed in triple quotes.
150 # Using rstrip() removes all trailing whitespace, which is what we want if there is something more than an endline.
151 ${final_text}= BuiltIn.Evaluate string.Template('''${template_as_string}'''.rstrip()).substitute(${mapping_as_string}) modules=string
152 BuiltIn.Log ${final_text}
153 [Return] ${final_text}
155 Put_Xml_Config_Module_Via_Restconf
156 [Arguments] ${xml_data} ${type} ${name}
157 [Documentation] Put new XML configuration to config:modules URI based on given module type and name.
159 Put_Xml_Config_Via_Restconf config:modules/module/${type}/${name} ${xml_data}
161 Put_Xml_Config_Service_Via_Restconf
162 [Arguments] ${xml_data} ${type} ${name}
163 [Documentation] Put new XML configuration to config:services URI based on given service type and instance name.
164 Put_Xml_Config_Via_Restconf config:services/service/${type}/config:instance/${name} ${xml_data}
166 Put_Xml_Config_Via_Restconf
167 [Arguments] ${uri_part} ${xml_data}
168 [Documentation] Put XML data to given controller-config URI, check reponse text is empty and status_code is one of allowed ones.
169 BuiltIn.Log ${uri_part}
170 BuiltIn.Log ${xml_data}
171 ${response}= RequestsLibrary.Put Request cvr_session ${uri_part} data=${xml_data}
172 BuiltIn.Log ${response.text}
173 BuiltIn.Log ${response.status_code}
174 BuiltIn.Should_Be_Empty ${response.text}
175 BuiltIn.Should_Contain ${allowed_status_codes} ${response.status_code}
177 Get_Xml_Config_Via_Restconf
178 [Arguments] ${uri_part}
179 [Documentation] Get XML data from given controller-config URI, check status_code is one of allowed ones, return response text.
180 BuiltIn.Log ${uri_part}
181 ${response}= RequestsLibrary.Get Request cvr_session ${uri_part} headers=${ACCEPT_XML}
182 BuiltIn.Log ${response.text}
183 BuiltIn.Log ${response.status_code}
184 BuiltIn.Should_Contain ${allowed_status_codes} ${response.status_code}
185 [Return] ${response.text}
187 Get_Json_Config_Via_Restconf
188 [Arguments] ${uri_part}
189 [Documentation] Get XML data from given controller-config URI, check status_code is one of allowed ones, return response text.
190 BuiltIn.Log ${uri_part}
191 ${response}= RequestsLibrary.Get Request cvr_session ${uri_part} headers=${ACCEPT_JSON}
192 BuiltIn.Log ${response.text}
193 BuiltIn.Log ${response.status_code}
194 BuiltIn.Should_Contain ${allowed_status_codes} ${response.status_code}
195 [Return] ${response.text}
197 Delete_Config_Via_Restconf
198 [Arguments] ${uri_part}
199 [Documentation] Delete resource at controller-config URI, check reponse text is empty and status_code is 204.
200 BuiltIn.Log ${uri_part}
201 ${response}= RequestsLibrary.Delete Request cvr_session ${uri_part}
202 BuiltIn.Log ${response.text}
203 BuiltIn.Should_Be_Empty ${response.text}
204 BuiltIn.Should_Contain ${allowed_status_codes} ${response.status_code}
206 Post_Xml_Config_Module_Via_Restconf
207 [Arguments] ${xml_data}
208 [Documentation] Post new XML configuration to config:modules.
210 Post_Xml_Config_Via_Restconf config:modules ${xml_data}
212 Post_Xml_Config_Service_Via_Restconf
213 [Arguments] ${xml_data}
214 [Documentation] Post new XML configuration to config:services.
215 Post_Xml_Config_Via_Restconf config:services ${xml_data}
217 Post_Xml_Config_Via_Restconf
218 [Arguments] ${uri_part} ${xml_data}
219 [Documentation] Post XML data to given controller-config URI, check reponse text is empty and status_code is 204.
220 # As seen in previous two Keywords, Post does not need long specific URI.
221 # But during Lithium development, Post ceased to do merge, so those Keywords do not work anymore.
222 # This Keyword can still be used with specific URI to create a new container and fail if a container was already present.
223 ${response}= RequestsLibrary.Post_Request cvr_session ${uri_part} data=${xml_data}
224 BuiltIn.Log ${response.text}
225 BuiltIn.Should_Be_Empty ${response.text}
226 # TODO: status_code is integrer, so compare to ${204}. Also, there is a Improvement for 201 to be a better code.
227 BuiltIn.Should_Be_Equal_As_Strings ${response.status_code} 204