Update Robot Framework format - step 1
[integration/test.git] / csit / libraries / Utils.robot
1 *** Settings ***
2 Documentation       General Utils library. This library has broad scope, it can be used by any robot system tests.
3
4 Library             SSHLibrary
5 Library             String
6 Library             DateTime
7 Library             Process
8 Library             Collections
9 Library             RequestsLibrary
10 Library             OperatingSystem
11 Library             ${CURDIR}/UtilLibrary.py
12 Resource            ${CURDIR}/SSHKeywords.robot
13 Resource            ${CURDIR}/TemplatedRequests.robot
14 Resource            ${CURDIR}/../variables/Variables.robot
15 Resource            ${CURDIR}/../variables/openflowplugin/Variables.robot
16
17
18 *** Variables ***
19 # TODO: Introduce ${tree_size} and use instead of 1 in the next line.
20 ${start}    sudo mn --controller=remote,ip=${ODL_SYSTEM_IP} --topo tree,1 --switch ovsk,protocols=OpenFlow13
21
22
23 *** Keywords ***
24 Start Mininet
25     [Documentation]    Basic setup/cleanup work that can be done safely before any system
26     ...    is run.
27     [Arguments]    ${system}=${TOOLS_SYSTEM_IP}    ${user}=${TOOLS_SYSTEM_USER}    ${password}=${TOOLS_SYSTEM_PASSWORD}    ${prompt}=${TOOLS_SYSTEM_PROMPT}    ${timeout}=30s
28     Log    Start the test on the base edition
29     Clean Mininet System
30     ${mininet_conn_id}=    Open Connection    ${system}    prompt=${prompt}    timeout=${timeout}
31     Set Suite Variable    ${mininet_conn_id}
32     SSHKeywords.Flexible Mininet Login    user=${user}    password=${password}
33     Execute Command    sudo ovs-vsctl set-manager ptcp:6644
34     Write    ${start}
35     Read Until    mininet>
36
37 Stop Mininet
38     [Documentation]    Cleanup/Shutdown work that should be done at the completion of all
39     ...    tests
40     [Arguments]    ${prompt}=${TOOLS_SYSTEM_PROMPT}
41     Log    Stop the test on the base edition
42     Switch Connection    ${mininet_conn_id}
43     Read
44     Write    exit
45     Read Until    ${prompt}
46     Close Connection
47
48 Report Failure Due To Bug
49     [Documentation]    Report that a test failed due to a known Bugzilla bug whose
50     ...    number is provided as an argument.
51     ...    Not FAILED (incl. SKIPPED) test are not reported.
52     ...    This keyword must be used in the [Teardown] setting of the affected test
53     ...    or as the first line of the test if FastFail module is not being
54     ...    used. It reports the URL of the bug on console and also puts it
55     ...    into the Robot log file.
56     [Arguments]    ${number}    ${include_bug_in_tags}=True
57     ${test_skipped}=    BuiltIn.Evaluate    len(re.findall('SKIPPED', """${TEST_MESSAGE}""")) > 0    modules=re
58     IF    ('${TEST_STATUS}' != 'FAIL') or ${test_skipped}    RETURN
59     Comment    Jira tickets are {PROJECT}-{NUMBER} while Bugzilla tickets are {NUMBER}
60     ${match}=    BuiltIn.Run Keyword And Return Status    Should Contain    ${number}    -
61     ${bug_url}=    BuiltIn.Set Variable If
62     ...    ${match}
63     ...    https://jira.opendaylight.org/browse/${number}
64     ...    https://bugs.opendaylight.org/show_bug.cgi?id=${number}
65     ${msg}=    BuiltIn.Set_Variable    This test fails due to ${bug_url}
66     ${newline}=    BuiltIn.Evaluate    chr(10)
67     BuiltIn.Set Test Message    ${msg}${newline}${newline}${TEST_MESSAGE}
68     BuiltIn.Log    ${msg}
69     IF    "${include_bug_in_tags}"=="True"    Set Tags    ${bug_url}
70
71 Report_Failure_And_Point_To_Linked_Bugs
72     [Documentation]    Report that a test failed and point to linked Bugzilla bug(s).
73     ...    Linked bugs must contain the ${reference} inside comments (workaround
74     ...    becasue of currently missing suitable field for external references and
75     ...    not correctly working the CONTENT MATCHES filter).
76     ...    Not FAILED (incl. SKIPPED) test are not reported.
77     ...    This keyword must be used in the [Teardown] setting of the affected test
78     ...    or as the first line of the test if FastFail module is not being
79     ...    used. It reports the URL of the bug on console and also puts it
80     ...    into the Robot log file.
81     ${test_skipped}=    BuiltIn.Evaluate    len(re.findall('SKIPPED', """${TEST_MESSAGE}""")) > 0    modules=re
82     IF    ('${TEST_STATUS}' != 'FAIL') or ${test_skipped}    RETURN
83     ${newline}=    BuiltIn.Evaluate    chr(10)
84     ${reference}=    String.Replace_String_Using_Regexp    ${SUITE_NAME}_${TEST_NAME}    [ /\.-]    _
85     ${reference}=    String.Convert_To_Lowercase    ${reference}
86     ${msg}=    BuiltIn.Set_Variable
87     ...    ... click for list of related bugs or create a new one if needed (with the${newline}"${reference}"${newline}reference somewhere inside)
88     ${bugs}=    BuiltIn.Set_Variable
89     ...    "https://bugs.opendaylight.org/buglist.cgi?f1=cf_external_ref&o1=substring&v1=${reference}&order=bug_status"
90     BuiltIn.Set Test Message    ${msg}${newline}${bugs}${newline}${newline}${TEST_MESSAGE}
91     BuiltIn.Log    ${msg}${newline}${bugs}
92
93 Check Nodes Stats
94     [Documentation]    A GET on the /node/${node} API is made and specific flow stat
95     ...    strings are checked for existence.
96     [Arguments]    ${node}    ${session}=session
97     ${resp}=    RequestsLibrary.Get Request
98     ...    ${session}
99     ...    ${RFC8040_NODES_API}/node=${node}?${RFC8040_OPERATIONAL_CONTENT}
100     Should Be Equal As Strings    ${resp.status_code}    200
101     Should Contain    ${resp.text}    flow-capable-node-connector-statistics
102     Should Contain    ${resp.text}    flow-table-statistics
103
104 Check For Specific Number Of Elements At URI
105     [Documentation]    A GET is made to the specified ${URI} and the specific count of a
106     ...    given element is done (as supplied by ${element} and ${expected_count})
107     [Arguments]    ${uri}    ${element}    ${expected_count}    ${session}=session
108     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}
109     Log    ${resp.text}
110     Should Be Equal As Strings    ${resp.status_code}    200
111     Should Contain X Times    ${resp.text}    ${element}    ${expected_count}
112
113 Log Content
114     [Arguments]    ${resp_content}
115     IF    '''${resp_content}''' != '${EMPTY}'
116         ${resp_json}=    RequestsLibrary.To Json    ${resp_content}    pretty_print=True
117     ELSE
118         ${resp_json}=    BuiltIn.Set Variable    ${EMPTY}
119     END
120     BuiltIn.Log    ${resp_json}
121     RETURN    ${resp_json}
122
123 Check For Elements At URI
124     [Documentation]    A GET is made at the supplied ${URI} and every item in the list of
125     ...    ${elements} is verified to exist in the response
126     [Arguments]    ${uri}    ${elements}    ${session}=session    ${pretty_print_json}=False
127     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}
128     IF    "${pretty_print_json}" == "True"
129         Log Content    ${resp.text}
130     ELSE
131         BuiltIn.Log    ${resp.text}
132     END
133     Should Be Equal As Strings    ${resp.status_code}    200
134     FOR    ${i}    IN    @{elements}
135         Should Contain    ${resp.text}    ${i}
136     END
137
138 Check For Elements Not At URI
139     [Documentation]    A GET is made at the supplied ${uri} and every item in the list of
140     ...    ${elements} is verified to NOT exist in the response. If ${check_for_null} is True
141     ...    return of 404 is treated as empty list. From Neon onwards, an empty list is always
142     ...    returned as null, giving 404 on rest call.
143     [Arguments]    ${uri}    ${elements}    ${session}=session    ${pretty_print_json}=False    ${check_for_null}=False
144     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}
145     IF    "${pretty_print_json}" == "True"
146         Log Content    ${resp.text}
147     ELSE
148         BuiltIn.Log    ${resp.text}
149     END
150     IF    "${check_for_null}" == "True"
151         IF    ${resp.status_code} == 404 or ${resp.status_code} == 409    RETURN
152     END
153     Should Be Equal As Strings    ${resp.status_code}    200
154     FOR    ${i}    IN    @{elements}
155         Should Not Contain    ${resp.text}    ${i}
156     END
157
158 Clean Mininet System
159     [Arguments]    ${system}=${TOOLS_SYSTEM_IP}
160     Run Command On Mininet    ${system}    sudo mn -c
161     Run Command On Mininet
162     ...    ${system}
163     ...    sudo ps -elf | egrep 'usr/local/bin/mn' | egrep python | awk '{print "sudo kill -9",$4}' | sh
164
165 Clean Up Ovs
166     [Documentation]    Cleans up the OVS instance and remove any existing common known bridges.
167     [Arguments]    ${system}=${TOOLS_SYSTEM_IP}
168     ${output}=    Run Command On Mininet    ${system}    sudo ovs-vsctl list-br
169     Log    ${output}
170     FOR    ${i}    IN    ${output}
171         Run Command On Mininet    ${system}    sudo ovs-vsctl --if-exists del-br ${i}
172     END
173     Run Command On Mininet    ${system}    sudo ovs-vsctl del-manager
174
175 Extract Value From Content
176     [Documentation]    Will take the given response content and return the value at the given index as a string
177     [Arguments]    ${content}    ${index}
178     ${JSON}=    Evaluate    json.loads('''${content}''')    json
179     ${value}=    Set Variable    ${JSON${index}}
180     RETURN    ${value}
181
182 Get Process ID Based On Regex On Remote System
183     [Documentation]    Uses ps to find a process that matches the supplied regex. Returns the PID of that process
184     ...    The ${regex_string_to_match_on} should produce a unique process otherwise the PID returned may not be
185     ...    the expected PID
186     [Arguments]    ${system}    ${regex_string_to_match_on}    ${user}=${TOOLS_SYSTEM_USER}    ${password}=${EMPTY}    ${prompt}=${DEFAULT_LINUX_PROMPT}    ${prompt_timeout}=30s
187     # doing the extra -v grep in this command to exclude the grep process itself from the output
188     ${cmd}=    Set Variable    ps -elf | grep -v grep | grep ${regex_string_to_match_on} | awk '{print $4}'
189     ${output}=    Run Command On Remote System
190     ...    ${system}
191     ...    ${cmd}
192     ...    user=${user}
193     ...    password=${password}
194     ...    prompt=${prompt}
195     ...    prompt_timeout=${prompt_timeout}
196     # ${output} contains the system prompt and all we want is the value of the number
197     ${pid}=    Fetch From Left    ${output}    \r
198     RETURN    ${pid}
199
200     # ...    Should there be * On Mininet and * On Controller specializations?
201     # TODO: Get Process * keywords have perhaps non-standard default credentials.
202
203 Get Process Thread Count On Remote System
204     [Documentation]    Executes the ps command to retrieve the lightweight process (aka thread) count.
205     [Arguments]    ${system}    ${pid}    ${user}=${TOOLS_SYSTEM_USER}    ${password}=${EMPTY}    ${prompt}=${DEFAULT_LINUX_PROMPT}    ${prompt_timeout}=30s
206     ${cmd}=    Set Variable    ps --no-headers -o nlwp ${pid}
207     ${output}=    Run Command On Remote System
208     ...    ${system}
209     ...    ${cmd}
210     ...    user=${user}
211     ...    password=${password}
212     ...    prompt=${prompt}
213     ...    prompt_timeout=${prompt_timeout}
214     # ${output} contains the system prompt and all we want is the value of the number
215     ${thread_count}=    Fetch From Left    ${output}    \r
216     RETURN    ${thread_count}
217
218 Strip Quotes
219     [Documentation]    Will strip ALL quotes from given string and return the new string
220     [Arguments]    ${string_to_strip}
221     ${string_to_return}=    Replace String    ${string_to_strip}    "    \    count=-1
222     RETURN    ${string_to_return}
223
224 Run Command On Remote System
225     [Documentation]    Reduces the common work of running a command on a remote system to a single higher level
226     ...    robot keyword, taking care to log in with a public key and. The command given is written and the return value
227     ...    depends on the passed argument values of return_stdout (default: True) and return_stderr (default: False).
228     ...    At least one should be True, or the keyword will exit and FAIL. If both are True, the resulting return value
229     ...    will be a two element list containing both. Otherwise the resulting return value is a string.
230     ...    No test conditions are checked.
231     [Arguments]    ${system}    ${cmd}    ${user}=${DEFAULT_USER}    ${password}=${EMPTY}    ${prompt}=${DEFAULT_LINUX_PROMPT}    ${prompt_timeout}=${DEFAULT_TIMEOUT}
232     ...    ${return_stdout}=True    ${return_stderr}=False
233     IF    "${return_stdout}"!="True" and "${return_stderr}"!="True"
234         Fail    At least one of {return_stdout} or {return_stderr} args should be set to True
235     END
236     ${current_ssh_connection}=    SSHLibrary.Get Connection
237     BuiltIn.Log
238     ...    Attempting to execute command "${cmd}" on remote system "${system}" by user "${user}" with keyfile pass "${keyfile_pass}" and prompt "${prompt}" and password "${password}"
239     ${conn_id}=    SSHLibrary.Open Connection    ${system}    prompt=${prompt}    timeout=${prompt_timeout}
240     SSHKeywords.Flexible SSH Login    ${user}    ${password}
241     ${stdout}    ${stderr}=    SSHLibrary.Execute Command    ${cmd}    return_stderr=True
242     SSHLibrary.Close Connection
243     Log    ${stderr}
244     IF    "${return_stdout}"!="True"    RETURN    ${stderr}
245     IF    "${return_stderr}"!="True"    RETURN    ${stdout}
246     RETURN    ${stdout}    ${stderr}
247     [Teardown]    SSHKeywords.Restore_Current_SSH_Connection_From_Index    ${current_ssh_connection.index}
248
249 Run Command On Remote System And Log
250     [Documentation]    Reduces the common work of running a command on a remote system to a single higher level
251     ...    robot keyword, taking care to log in with a public key and. The command given is written
252     ...    and the output returned. No test conditions are checked.
253     [Arguments]    ${system}    ${cmd}    ${user}=${DEFAULT_USER}    ${password}=${EMPTY}    ${prompt}=${DEFAULT_LINUX_PROMPT}    ${prompt_timeout}=${DEFAULT_TIMEOUT}
254     ${output}=    Run Command On Remote System    ${system}    ${cmd}    ${user}    ${password}    ${prompt}
255     ...    ${prompt_timeout}
256     Log    ${output}
257     RETURN    ${output}
258
259 Run Command On Mininet
260     [Documentation]    Call Run Comand On Remote System, but with default values suitable for Mininet machine.
261     [Arguments]    ${system}=${TOOLS_SYSTEM_IP}    ${cmd}=echo    ${user}=${TOOLS_SYSTEM_USER}    ${password}=${TOOLS_SYSTEM_PASSWORD}    ${prompt}=${TOOLS_SYSTEM_PROMPT}
262     BuiltIn.Run Keyword And Return
263     ...    Run Command On Remote System
264     ...    ${system}
265     ...    ${cmd}
266     ...    ${user}
267     ...    ${password}
268     ...    prompt=${prompt}
269
270 Run Command On Controller
271     [Documentation]    Call Run Comand On Remote System, but with default values suitable for Controller machine.
272     [Arguments]    ${system}=${ODL_SYSTEM_IP}    ${cmd}=echo    ${user}=${ODL_SYSTEM_USER}    ${password}=${ODL_SYSTEM_PASSWORD}    ${prompt}=${ODL_SYSTEM_PROMPT}
273     BuiltIn.Run Keyword And Return
274     ...    Run Command On Remote System
275     ...    ${system}
276     ...    ${cmd}
277     ...    ${user}
278     ...    ${password}
279     ...    prompt=${prompt}
280
281 Run Command On Existing Connection
282     [Documentation]    Switch to and run command on an already existing SSH connection and switch back
283     [Arguments]    ${conn_id}=${EMPTY}    ${cmd}=echo    ${return_stdout}=True    ${return_stderr}=False
284     IF    "${return_stdout}"!="True" and "${return_stderr}"!="True"
285         Fail    At least one of {return_stdout} or {return_stderr} args should be set to True
286     END
287     ${current_ssh_connection}=    SSHLibrary.Get Connection
288     BuiltIn.Log    Attempting to execute command "${cmd}" on existing connection "${conn_id}
289     SSHLibrary.Switch Connection    ${conn_id}
290     ${stdout}    ${stderr}=    SSHLibrary.Execute Command    ${cmd}    return_stderr=True
291     Log    ${stderr}
292     IF    "${return_stdout}"!="True"    RETURN    ${stderr}
293     IF    "${return_stderr}"!="True"    RETURN    ${stdout}
294     RETURN    ${stdout}    ${stderr}
295     [Teardown]    SSHKeywords.Restore_Current_SSH_Connection_From_Index    ${current_ssh_connection.index}
296
297 Verify File Exists On Remote System
298     [Documentation]    Will create connection with public key and will PASS if the given ${file} exists,
299     ...    otherwise will FAIL
300     [Arguments]    ${system}    ${file}    ${user}=${TOOLS_SYSTEM_USER}    ${password}=${TOOLS_SYSTEM_PASSWORD}    ${prompt}=${DEFAULT_LINUX_PROMPT}    ${prompt_timeout}=5s
301     ${conn_id}=    Open Connection    ${system}    prompt=${prompt}    timeout=${prompt_timeout}
302     SSHKeywords.Flexible SSH Login    ${user}    ${password}
303     SSHLibrary.File Should Exist    ${file}
304     Close Connection
305
306 Check Karaf Log File Does Not Have Messages
307     [Documentation]    Fails if the provided ${message} is found in the karaf.log file. Uses grep to search. The
308     ...    karaf.log file can be overridden with ${log_file} to be any file on the given system @ ${ip}
309     [Arguments]    ${ip}    ${message}    ${user}=${ODL_SYSTEM_USER}    ${password}=${ODL_SYSTEM_PASSWORD}    ${prompt}=${ODL_SYSTEM_PROMPT}    ${log_file}=${WORKSPACE}/${BUNDLEFOLDER}/data/log/karaf.log
310     ${output}=    Run Command On Controller
311     ...    ${ip}
312     ...    grep -c '${message}' ${log_file}
313     ...    user=${user}
314     ...    password=${password}
315     ...    prompt=${prompt}
316     Should Be Equal As Strings    ${output}    0
317
318 Verify Controller Is Not Dead
319     [Documentation]    Will execute any tests to verify the controller is not dead. Some checks are
320     ...    Out Of Memory Execptions.
321     [Arguments]    ${controller_ip}=${ODL_SYSTEM_IP}
322     Check Karaf Log File Does Not Have Messages    ${controller_ip}    java.lang.OutOfMemoryError
323     # TODO: Should Verify Controller * keywords also accept user, password, prompt and karaf_log arguments?
324
325 Verify Controller Has No Null Pointer Exceptions
326     [Documentation]    Will execute any tests to verify the controller is not having any null pointer eceptions.
327     [Arguments]    ${controller_ip}=${ODL_SYSTEM_IP}
328     Check Karaf Log File Does Not Have Messages    ${controller_ip}    java.lang.NullPointerException
329
330 Verify Controller Has No Runtime Exceptions
331     [Documentation]    Will execute any tests to verify the controller is not having any runtime eceptions.
332     [Arguments]    ${controller_ip}=${ODL_SYSTEM_IP}
333     Check Karaf Log File Does Not Have Messages    ${controller_ip}    java.lang.RuntimeException
334
335 Get Epoch Time
336     [Documentation]    Get the Epoc time from MM/DD/YYYY HH:MM:SS
337     [Arguments]    ${time}
338     ${epoch_time}=    Convert Date    ${time}    epoch    exclude_milles=True    date_format=%m/%d/%Y %H:%M:%S
339     ${epoch_time}=    Convert To Integer    ${epoch_time}
340     RETURN    ${epoch_time}
341
342 Remove Space on String
343     [Documentation]    Remove the empty space from given string.count is optional,if its given
344     ...    that many occurence of space will be removed from left
345     [Arguments]    ${str}    ${count}=-1
346     ${x}=    Convert To String    ${str}
347     ${x}=    Replace String    ${str}    ${SPACE}    ${EMPTY}    count=${count}
348     RETURN    ${x}
349
350 Split Value from String
351     [Documentation]    Split the String based on given splitter and return as list
352     [Arguments]    ${str}    ${splitter}
353     @{x}=    Split String    ${str}    ${splitter}
354     RETURN    @{x}
355
356 Concatenate the String
357     [Documentation]    Catenate the two non-string objects and return as String
358     [Arguments]    ${str1}    ${str2}
359     ${str1}=    Convert to String    ${str1}
360     ${str2}=    Convert to String    ${str2}
361     ${output}=    Catenate    ${str1}    ${str2}
362     RETURN    ${output}
363
364 Post Elements To URI
365     [Documentation]    Perform a POST rest operation, using the URL and data provided
366     [Arguments]    ${rest_uri}    ${data}    ${headers}=${headers}    ${session}=session
367     ${resp}=    RequestsLibrary.Post Request    ${session}    ${rest_uri}    data=${data}    headers=${headers}
368     Log    ${resp.text}
369     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
370
371 Remove All Elements At URI
372     [Arguments]    ${uri}    ${session}=session
373     ${resp}=    RequestsLibrary.Delete Request    ${session}    ${uri}
374     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
375
376 Remove All Elements At URI And Verify
377     [Arguments]    ${uri}    ${session}=session
378     ${resp}=    RequestsLibrary.Delete Request    ${session}    ${uri}
379     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
380     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}
381     Should Contain    ${DELETED_STATUS_CODES}    ${resp.status_code}
382
383 Remove All Elements If Exist
384     [Documentation]    Delete all elements from an URI if the configuration was not empty
385     [Arguments]    ${uri}    ${session}=session
386     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}
387     IF    '${resp.status_code}'!='404' and '${resp.status_code}'!='409'
388         Remove All Elements At URI    ${uri}    ${session}
389     END
390
391 Add Elements To URI From File
392     [Documentation]    Put data from a file to a URI
393     [Arguments]    ${dest_uri}    ${data_file}    ${headers}=${headers}    ${session}=session
394     ${body}=    OperatingSystem.Get File    ${data_file}
395     ${resp}=    RequestsLibrary.Put Request    ${session}    ${dest_uri}    data=${body}    headers=${headers}
396     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
397
398 Add Elements To URI From File And Verify
399     [Documentation]    Put data from a file to a URI and verify the HTTP response
400     [Arguments]    ${dest_uri}    ${data_file}    ${headers}=${headers}    ${session}=session
401     ${body}=    OperatingSystem.Get File    ${data_file}
402     Add Elements to URI And Verify    ${dest_uri}    ${body}    ${headers}    ${session}
403
404 Add Elements To URI And Verify
405     [Documentation]    Put data to a URI and verify the HTTP response
406     [Arguments]    ${dest_uri}    ${data}    ${headers}=${headers}    ${session}=session
407     ${resp}=    RequestsLibrary.Put Request    ${session}    ${dest_uri}    ${data}    headers=${headers}
408     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
409     ${resp}=    RequestsLibrary.Get Request    ${session}    ${dest_uri}
410     Should Not Contain    ${DELETED_STATUS_CODES}    ${resp.status_code}
411
412 Add Elements To URI From File And Check Validation Error
413     [Documentation]    Shorthand for PUTting data from file and expecting status code 400.
414     [Arguments]    ${dest_uri}    ${data_file}    ${headers}=${headers}    ${session}=session
415     BuiltIn.Comment    TODO: Does this have any benefits, considering TemplatedRequests can also do this in one line?
416     ${body}=    OperatingSystem.Get File    ${data_file}
417     ${resp}=    RequestsLibrary.Put Request    ${session}    ${dest_uri}    data=${body}    headers=${headers}
418     Should Contain    ${DATA_VALIDATION_ERROR}    ${resp.status_code}
419
420 Add Elements To URI From File And Check Server Error
421     [Documentation]    Shorthand for PUTting data from file and expecting status code 500.
422     ...    Consider opening a Bug against ODL, as in most test cases, 400 is the http code to expect.
423     [Arguments]    ${dest_uri}    ${data_file}    ${headers}=${headers}    ${session}=session
424     BuiltIn.Comment    TODO: Does this have any benefits, considering TemplatedRequests can also do this in one line?
425     ${body}=    OperatingSystem.Get File    ${data_file}
426     ${resp}=    RequestsLibrary.Put Request    ${session}    ${dest_uri}    data=${body}    headers=${headers}
427     Should Contain    ${INTERNAL_SERVER_ERROR}    ${resp.status_code}
428
429 Post Elements To URI From File
430     [Arguments]    ${dest_uri}    ${data_file}    ${headers}=${headers}    ${session}=session
431     ${body}=    OperatingSystem.Get File    ${data_file}
432     ${resp}=    RequestsLibrary.Post Request    ${session}    ${dest_uri}    data=${body}    headers=${headers}
433     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
434
435 Run Process With Logging And Status Check
436     [Documentation]    Execute an OS command, log STDOUT and STDERR output and check exit code to be 0
437     [Arguments]    @{proc_args}
438     ${result}=    Run Process    @{proc_args}
439     Log    ${result.stdout}
440     Log    ${result.stderr}
441     Should Be Equal As Integers    ${result.rc}    0
442     RETURN    ${result}
443
444 Get Data From URI
445     [Documentation]    Issue a GET request and return the data obtained or on error log the error and fail.
446     ...    Issues a GET request for ${uri} in ${session} using headers from
447     ...    ${headers}. If the request returns a HTTP error, fails. Otherwise
448     ...    returns the data obtained by the request.
449     [Arguments]    ${session}    ${uri}    ${headers}=${NONE}
450     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}    ${headers}
451     IF    ${resp.status_code} == 200    RETURN    ${resp.text}
452     Builtin.Log    ${resp.text}
453     Builtin.Fail    The request failed with code ${resp.status_code}
454
455 Get URI And Verify
456     [Documentation]    Issue a GET request and verify a successfull HTTP return.
457     ...    Issues a GET request for ${uri} in ${session} using headers from ${headers}.
458     [Arguments]    ${uri}    ${session}=session    ${headers}=${NONE}
459     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}    ${headers}
460     Builtin.Log    ${resp.status_code}
461     Should Contain    ${ALLOWED_STATUS_CODES}    ${resp.status_code}
462
463 No Content From URI
464     [Documentation]    Issue a GET request and return on error 404 (No content) or will fail and log the content.
465     ...    Issues a GET request for ${uri} in ${session} using headers from
466     ...    ${headers}. If the request returns a HTTP error, fails. Otherwise
467     ...    returns the data obtained by the request.
468     [Arguments]    ${session}    ${uri}    ${headers}=${NONE}
469     ${resp}=    RequestsLibrary.Get Request    ${session}    ${uri}    ${headers}
470     IF    ${resp.status_code} == 404 or ${resp.status_code} == 409    RETURN
471     Builtin.Log    ${resp.text}
472     Builtin.Fail    The request failed with code ${resp.status_code}
473
474 Get Index From List Of Dictionaries
475     [Documentation]    Extract index for the dictionary in a list that contains a key-value pair. Returns -1 if key-value is not found.
476     [Arguments]    ${dictionary_list}    ${key}    ${value}
477     ${length}=    Get Length    ${dictionary_list}
478     ${index}=    Set Variable    -1
479     FOR    ${i}    IN RANGE    ${length}
480         ${dictionary}=    Get From List    ${dictionary_list}    ${i}
481         IF    """${dictionary}[${key}]""" == """${value}"""
482             Set Test Variable    ${index}    ${i}
483         END
484     END
485     RETURN    ${index}
486
487 Check Item Occurrence
488     [Documentation]    Check string for occurrences of items expressed in a list of dictionaries {item=occurrences}. 0 occurences means item is not present.
489     [Arguments]    ${string}    ${dictionary_item_occurrence}
490     FOR    ${item}    IN    @{dictionary_item_occurrence}
491         Should Contain X Times    ${string}    ${item}    ${dictionary_item_occurrence}[${item}]
492     END
493
494 Post Log Check
495     [Documentation]    Post body to ${uri}, log response content, and check status
496     [Arguments]    ${uri}    ${body}    ${session}=session    ${status_codes}=200
497     ${resp}=    RequestsLibrary.Post Request    ${session}    ${uri}    ${body}
498     Log    ${resp.text}
499     TemplatedRequests.Check Status Code    ${resp}    ${status_codes}
500     RETURN    ${resp}
501
502 Get Log File Name
503     [Documentation]    Get the name of the suite sanitized to be usable as a part of filename.
504     ...    These names are used to constructs names of the log files produced
505     ...    by the testing tools so two suites using a tool wont overwrite the
506     ...    log files if they happen to run in one job.
507     [Arguments]    ${testtool}    ${testcase}=${EMPTY}
508     ${name}=    BuiltIn.Evaluate    """${SUITE_NAME}""".replace(" ","-").replace("/","-").replace(".","-")
509     ${suffix}=    BuiltIn.Set_Variable_If    '${testcase}' != ''    --${testcase}    ${EMPTY}
510     ${date}=    DateTime.Get Current Date
511     ${timestamp}=    DateTime.Convert Date    ${date}    epoch
512     RETURN    ${testtool}--${name}${suffix}.${timestamp}.log
513
514 Set_User_Configurable_Variable_Default
515     [Documentation]    Set a default value for an user configurable variable.
516     ...    This keyword is needed if your default value is calculated using
517     ...    a complex expression which needs BuiltIn.Evaluate or even more
518     ...    complex keywords. It sets the variable ${name} (the name of the
519     ...    variable MUST be specified WITHOUT the ${} syntactic sugar due
520     ...    to limitations of Robot Framework) to ${value} but only if the
521     ...    variable ${name} was not set previously. This keyword is intended
522     ...    for user configurable variables which are supposed to be set only
523     ...    with pybot -v; calling this keyword on a variable that was already
524     ...    set by another keyword will silently turn the call into a NOP and
525     ...    thus is a bug in the suite or resource trying to call this
526     ...    keyword.
527     [Arguments]    ${name}    ${value}
528     # TODO: Figure out how to make the ${value} evaluation "lazy" (meaning
529     #    evaluating it only when the user did not set anything and thus the
530     #    default is needed). This might be needed to avoid potentially costly
531     #    keyword invocations when they are not needed. Currently no need for
532     #    this was identified, thus leaving it here as a TODO. Based on
533     #    comments the best approach would be to create another keyword that
534     #    expects a ScalarClosure in the place of ${value} and calls the
535     #    closure to get the value but only if the value is needed).
536     #    The best idea how to implement this "laziness" would be to have the
537     #    used to define another keyword that will be responsible for getting
538     #    the default value and then passing the name of this getter keyword
539     #    to this keyword. Then this keyword would call the getter (to obtain
540     #    the expensive default value) only if it discovers that this value
541     #    is really needed (because the variable is not set yet).
542     # TODO: Is the above TODO really necessary? Right now we don't have any
543     #    examples of "expensive default values" where to obtain the default
544     #    value is so expensive on resources (e.g. need to SSH somewhere to
545     #    check something) that we would want to skip the calculation if the
546     #    variable for which it is needed has a value already provided by the
547     #    user using "pybot -v" or something. One example would be
548     #    JAVA_HOME if it would be designed as user-configurable variable
549     #    (currently it is not; users can specify "use jdk7" or "use jdk8"
550     #    but not "use the jdk over there"; and there actually is no JAVA_HOME
551     #    present in the resource, rather the Java invocation command uses the
552     #    Java invocation with a full path). The default value of JAVA_HOME
553     #    has to be obtained by issuing commands on the SSH connection where
554     #    the resulting Java invocation command will be used (to check
555     #    multiple candidate paths until one that fits is found) and we could
556     #    skip all this checking if a JAVA_HOME was supplied by the user using
557     #    "pybot -v".
558     ${value}=    BuiltIn.Get_Variable_Value    \${${name}}    ${value}
559     BuiltIn.Set_Suite_Variable    \${${name}}    ${value}
560
561 Convert_To_Minutes
562     [Documentation]    Convert a Robot time string to an integer expressing the time in minutes, rounded up
563     ...    This is a wrapper around DateTime.Convert_Time which does not
564     ...    provide this functionality directly nor is even able to produce
565     ...    an integer directly. It is needed for RestPerfClient which
566     ...    cannot accept floats for its --timeout parameter and interprets
567     ...    the value supplied in this parameter in minutes.
568     [Arguments]    ${time}
569     ${seconds}=    DateTime.Convert_Time    ${time}    result_format=number
570     ${minutes}=    BuiltIn.Evaluate    int(math.ceil(${seconds}/60.0))    modules=math
571     RETURN    ${minutes}
572
573 Write Commands Until Expected Prompt
574     [Documentation]    quick wrapper for Write and Read Until Prompt Keywords to make test cases more readable
575     [Arguments]    ${cmd}    ${prompt}    ${timeout}=${DEFAULT_TIMEOUT}
576     BuiltIn.Log    cmd: ${cmd}
577     SSHLibrary.Set Client Configuration    timeout=${timeout}
578     SSHLibrary.Read
579     SSHLibrary.Write    ${cmd}
580     ${output}=    SSHLibrary.Read Until    ${prompt}
581     RETURN    ${output}
582
583 Write Commands Until Expected Regexp
584     [Documentation]    quick wrapper for Write and Read Until Prompt Keywords to make test cases more readable
585     [Arguments]    ${cmd}    ${regexp}    ${timeout}=${DEFAULT_TIMEOUT}
586     BuiltIn.Log    cmd: ${cmd}
587     SSHLibrary.Set Client Configuration    timeout=${timeout}
588     SSHLibrary.Read
589     SSHLibrary.Write    ${cmd}
590     ${output}=    SSHLibrary.Read Until Regexp    ${regexp}
591     RETURN    ${output}
592
593 Install Package On Ubuntu System
594     [Documentation]    Keyword to install packages for testing to Ubuntu Mininet VM
595     [Arguments]    ${package_name}    ${system}=${TOOLS_SYSTEM_IP}    ${user}=${TOOLS_SYSTEM_USER}    ${password}=${TOOLS_SYSTEM_PASSWORD}    ${prompt}=${DEFAULT_LINUX_PROMPT}    ${prompt_timeout}=30s
596     Log    Keyword to install package to Mininet Ubuntu VM
597     Open Connection    ${system}    prompt=${prompt}    timeout=${prompt_timeout}
598     SSHKeywords.Flexible Mininet Login    user=${user}    password=${password}
599     Write    sudo apt-get install -y ${package_name}
600     Read Until    ${prompt}
601
602 Json Parse From String
603     [Documentation]    Parse given plain string into json (dictionary)
604     [Arguments]    ${plain_string_with_json}
605     ${json_data}=    Evaluate    json.loads('''${plain_string_with_json}''')    json
606     RETURN    ${json_data}
607
608 Json Parse From File
609     [Documentation]    Parse given file content into json (dictionary)
610     [Arguments]    ${json_file}
611     ${json_plain_string}=    OperatingSystem.Get file    ${json_file}
612     ${json_data}=    Json Parse From String    ${json_plain_string}
613     RETURN    ${json_data}
614
615 Modify Iptables On Remote System
616     [Documentation]    Wrapper keyword to run iptables with any given ${iptables_rule} string on the remote system given
617     ...    by ${remote_system_ip}. The iptables listing will be output before and after the command is run
618     [Arguments]    ${remote_system_ip}    ${iptables_rule}    ${user}=${ODL_SYSTEM_USER}    ${password}=${ODL_SYSTEM_PASSWORD}    ${prompt}=${ODL_SYSTEM_PROMPT}
619     ${list_iptables_command}=    BuiltIn.Set Variable    sudo /sbin/iptables -L -n
620     ${output}=    Utils.Run Command On Remote System
621     ...    ${remote_system_ip}
622     ...    ${list_iptables_command}
623     ...    ${user}
624     ...    ${password}
625     ...    prompt=${prompt}
626     BuiltIn.Log    ${output}
627     Utils.Run Command On Remote System
628     ...    ${remote_system_ip}
629     ...    sudo /sbin/iptables ${iptables_rule}
630     ...    ${user}
631     ...    ${password}
632     ...    prompt=${prompt}
633     ${output}=    Utils.Run Command On Remote System
634     ...    ${remote_system_ip}
635     ...    ${list_iptables_command}
636     ...    ${user}
637     ...    ${password}
638     ...    prompt=${prompt}
639     BuiltIn.Log    ${output}
640
641 Get_Sysstat_Statistics
642     [Documentation]    Store current connection index, open new connection to ip_address. Run command to get sysstat results from script,
643     ...    which is running on all children nodes. Returns cpu, network, memory usage statistics from the node for each 10 minutes
644     ...    that node was running. Used for debug purposes. Returns whole output of sysstat.
645     [Arguments]    ${ip_address}=${ODL_SYSTEM_IP}
646     ${current_connection}=    SSHLibrary.Get_Connection
647     SSHKeywords.Open_Connection_To_ODL_System    ${ip_address}
648     SSHLibrary.Write    sar -A -f /var/log/sa/sa*
649     ${output}=    SSHLibrary.Read_Until_Prompt
650     BuiltIn.Log    ${output}
651     SSHLibrary.Close_Connection
652     RETURN    ${output}
653     [Teardown]    SSHKeywords.Restore_Current_SSH_Connection_From_Index    ${current_connection.index}
654
655 Check Diagstatus
656     [Documentation]    GET http://${ip_address}:${RESTCONFPORT}/diagstatus and return the response. ${check_status}
657     ...    and ${expected_status_code} can be used to ignore the status code, or validate any status code value.
658     ...    By default, this keyword will pass if the status code returned is 200, and fail otherwise.
659     [Arguments]    ${ip_address}=${ODL_SYSTEM_IP}    ${check_status}=True    ${expected_status}=${200}
660     RequestsLibrary.Create Session    diagstatus_session    http://${ip_address}:${RESTCONFPORT}
661     ${resp}=    RequestsLibrary.Get Request    diagstatus_session    /diagstatus
662     IF    "${check_status}" == "True"
663         BuiltIn.Should Be Equal As Strings    ${resp.status_code}    ${expected_status}
664     END
665     RETURN    ${resp}
666
667 Download File On Openstack Node
668     [Documentation]    Download a file from web to the node. the input will be a session ID with established SSH connection.
669     [Arguments]    ${conn_id}    ${save_file_name}    ${url}
670     SSHLibrary.Switch Connection    ${conn_id}
671     Utils.Write Commands Until Expected Prompt
672     ...    wget -O /tmp/${save_file_name} ${url}
673     ...    ${DEFAULT_LINUX_PROMPT_STRICT}