VOL-2642 Add a Makefile, tests, and virtualenv
Convert common python and robot into a CORDRobot python module that can
be installed via standard python tools (pip) and from PyPI
Uses a fork of https://github.com/rasjani/robotframework-importresource,
which has been backported to Python 3.5 (used in Ubuntu 16.04
executors).
Reformatted and moved keywords so resource files are scoped to a
specific topic.
Added tox tests for library consistency
- flake8
- pylint
- robotframework-lint
- Ran robot against installed library to verify it can be loaded and
used
Added basic lint and tests to whole repo
Removed old tests:
- CORD <6.x era: SanityPhyPOD.robot, and onosUtils.py
Change-Id: I61265a9fb04034a086e20be1f7236a8793a218aa
diff --git a/cord-robot/CORDRobot/rf-resources/utils.resource b/cord-robot/CORDRobot/rf-resources/utils.resource
new file mode 100644
index 0000000..d85fa20
--- /dev/null
+++ b/cord-robot/CORDRobot/rf-resources/utils.resource
@@ -0,0 +1,222 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+Documentation Library for various utilities
+Library SSHLibrary
+Library String
+Library DateTime
+Library Process
+Library Collections
+Library RequestsLibrary
+
+*** Keywords ***
+Login To Remote System
+ [Documentation] SSH into a remote host (and into a container on that host if container_type
+ ... and container_name are specified) and returns connection ID
+ [Arguments] ${ip} ${user} ${pass}=${None}
+ ... ${container_type}=${None} ${container_name}=${None}
+ ... ${prompt}=~$ ${prompt_timeout}=15s ${container_prompt}=#
+ ${conn_id}= SSHLibrary.Open Connection ${ip} prompt=${prompt} timeout=${prompt_timeout}
+ Run Keyword If '${pass}' != '${None}'
+ ... SSHLibrary.Login ${user} ${pass}
+ ... ELSE
+ ... SSHLibrary.Login With Public Key ${user} %{HOME}/.ssh/id_rsa
+ # Login to the lxc container
+ Run Keyword If '${container_type}' == 'LXC' Run Keywords
+ ... SSHLibrary.Write lxc exec ${container_name} /bin/bash
+ ... AND
+ ... SSHLibrary.Read Until ${container_prompt}
+ ... AND
+ ... SSHLibrary.Set Client Configuration prompt=${container_prompt}
+ # Login to the k8s container
+ # FIXME: This fails if /bin/bash isn't installed in the container, run on command
+ Run Keyword If '${container_type}' == 'K8S' Run Keywords
+ ... SSHLibrary.Write kubectl -n $(kubectl get pods --all-namespaces | grep ${container_name} | awk '{print $1}') exec ${container_name} -it /bin/bash
+ ... AND
+ ... SSHLibrary.Read Until ${container_prompt}
+ ... AND
+ ... SSHLibrary.Set Client Configuration prompt=${container_prompt}
+ # Try to switch to root user
+ # FIXME: Is is useful in the LXC/K8S cases?
+ ${conn}= SSHLibrary.Get Connection ${conn_id}
+ Run Keyword And Ignore Error
+ ... SSHLibrary.Write sudo -s
+ ${output}= SSHLibrary.Read Until Regexp \#|${conn.prompt}|password for ${user}:
+ Run Keyword If 'password for ${user}:' not in '''${output}'''
+ ... Return From Keyword ${conn_id}
+ SSHLibrary.Set Client Configuration prompt=\#
+ SSHLibrary.Write ${pass}
+ SSHLibrary.Read Until Prompt
+ [Return] ${conn_id}
+
+Logout From Remote System
+ [Documentation] Exit from the SSH session to a remote host
+ [Arguments] ${conn_id}
+ SSHLibrary.Switch Connection ${conn_id}
+ SSHLibrary.Close Connection
+
+Run Command On Remote System
+ [Documentation] Executes a command on remote host and returns output
+ [Arguments] ${cmd} ${conn_id} ${user} ${pass}=${None}
+ ${conn}= SSHLibrary.Get Connection ${conn_id}
+ SSHLibrary.Switch Connection ${conn_id}
+ SSHLibrary.Write ${cmd}
+ ${output}= SSHLibrary.Read Until Regexp ${conn.prompt}|password for ${user}:
+ Run Keyword If 'password for ${user}:' not in '''${output}'''
+ ... Return From Keyword ${output}
+ SSHLibrary.Write ${pass}
+ ${output}= SSHlibrary.Read Until Prompt
+ [Return] ${output}
+
+Login And Run Command On Remote System
+ [Documentation] SSH into a remote host (and into a container on that host if container_type
+ ... and container_name are specified), switch to root user, executes command, return output
+ [Arguments] ${cmd} ${ip} ${user} ${pass}=${None}
+ ... ${container_type}=${None} ${container_name}=${None}
+ ... ${prompt}=~$ ${prompt_timeout}=50s ${container_prompt}=#
+ ${conn_id} Login To Remote System ${ip} ${user} ${pass}
+ ... ${container_type} ${container_name}
+ ... ${prompt} ${prompt_timeout} ${container_prompt}
+ ${output}= Run Command On Remote System ${cmd} ${conn_id} ${user} ${pass}
+ Log ${output}
+ # FIXME: Look into persisting SSH connection rather than tearing it up/down repeatedly
+ Logout From Remote System ${conn_id}
+ [Return] ${output}
+
+Execute Command Locally
+ [Documentation] Superfluous, use the 'Run' keyword instead which this wraps
+ [Arguments] ${cmd}
+ ${output}= Run ${cmd}
+ [Return] ${output}
+
+Get Docker Container ID
+ [Documentation] Retrieves the id of the requested docker container running locally
+ [Arguments] ${container_name}
+ ${container_id}= Run docker ps | grep ${container_name} | awk '{print $1}'
+ Log ${container_id}
+ [Return] ${container_id}
+
+Remove Value From List
+ [Documentation] Removes a value from a dictionary
+ [Arguments] ${list} ${val}
+ ${length}= Get Length ${list}
+ FOR ${INDEX} IN RANGE 0 ${length}
+ Log ${list[${INDEX}]}
+ ${value}= Get Dictionary Values ${list[${INDEX}]}
+ Log ${value[0]}
+ Run Keyword If '${value[0]}' == '${val}' Remove From List ${list} ${INDEX}
+ Run Keyword If '${value[0]}' == '${val}' Exit For Loop
+ END
+
+Test Ping
+ [Documentation] SSH's into src and attempts to ping dest.
+ ... Status determines if ping should pass | fail
+ [Arguments] ${status} ${src} ${user} ${pass}
+ ... ${dest} ${interface} ${prompt}=$ ${prompt_timeout}=60s
+ ${conn_id}= SSHLibrary.Open Connection
+ ... ${src} prompt=${prompt} timeout=${prompt_timeout}
+ SSHLibrary.Login ${user} ${pass}
+ ${result}= SSHLibrary.Execute Command
+ ... ping -I ${interface} -c 5 ${dest}
+ SSHLibrary.Close Connection
+ Log ${result}
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Contain ${result} 64 bytes
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Contain ${result} 0% packet loss
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Not Contain ${result} 100% packet loss
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Not Contain ${result} 80% packet loss
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Not Contain ${result} 60% packet loss
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Not Contain ${result} 40% packet loss
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Not Contain ${result} 20% packet loss
+ Run Keyword If '${status}' == 'PASS'
+ ... Should Not Contain ${result} Destination Host Unreachable
+ Run Keyword If '${status}' == 'FAIL'
+ ... Should Not Contain ${result} 64 bytes
+ Run Keyword If '${status}' == 'FAIL'
+ ... Should Contain ${result} 100% packet loss
+ Log To Console \n ${result}
+
+Check Ping Result
+ [Documentation] Check the output of the 'ping' command
+ [Arguments] ${reachable} ${result}
+ Run Keyword If '${reachable}' == 'True'
+ ... Should Contain ${result} 64 bytes
+ Run Keyword If '${reachable}' == 'True'
+ ... Should Contain Any ${result} 0% packet loss 0.0% packet loss
+ Run Keyword If '${reachable}' == 'True'
+ ... Should Not Contain Any ${result} 100% packet loss 100.0% packet loss
+ Run Keyword If '${reachable}' == 'False'
+ ... Should Not Contain ${result} 64 bytes
+ Run Keyword If '${reachable}' == 'False'
+ ... Should Contain Any ${result} 100% packet loss 100.0% packet loss
+
+Check Ping
+ [Documentation] Run 'ping' on remote system and check output
+ [Arguments] ${ping_should_pass} ${dst_ip} ${iface} ${ip}
+ ... ${user} ${pass}=${None} ${container_type}=${None} ${container_name}=${None}
+ ${result}= Login And Run Command On Remote System
+ ... ping -I ${iface} -c 3 ${dst_ip}
+ ... ${ip} ${user} ${pass} ${container_type} ${container_name}
+ Check Ping Result ${ping_should_pass} ${result}
+
+Check Remote System Reachability
+ [Documentation] Check if the specified IP address is reachable or not
+ [Arguments] ${reachable} ${ip}
+ ${result}= Run ping -c 3 ${ip}
+ Check Ping Result ${reachable} ${result}
+
+Kill Linux Process
+ [Documentation] Kill a process on a remote system
+ [Arguments] ${process} ${ip} ${user} ${pass}=${None}
+ ... ${container_type}=${None} ${container_name}=${None}
+ ${rc}= Login And Run Command On Remote System
+ ... kill $(ps aux | grep '${process}' | awk '{print $2}'); echo $?
+ ... ${ip} ${user} ${pass} ${container_type} ${container_name}
+ Should Be Equal As Integers ${rc} 0
+
+Check Remote File Contents
+ [Documentation] Check if file on remote system matches a `grep` regex
+ [Arguments] ${file_should_exist} ${file} ${pattern}
+ ... ${ip} ${user} ${pass}=${None}
+ ... ${container_type}=${None} ${container_name}=${None} ${prompt}=~$
+ ${output}= Login And Run Command On Remote System
+ ... cat ${file} | grep '${pattern}'
+ ... ${ip} ${user} ${pass} ${container_type} ${container_name} ${prompt}
+ # FIXME: Comparison against truthy value
+ Run Keyword If '${file_should_exist}' == 'True'
+ ... Should Contain ${output} ${pattern}
+ ... ELSE
+ ... Should Not Contain ${output} ${pattern}
+
+Set Deployment Config Variables
+ [Documentation] Parses through the given deployment config and sets all the "src" and "dst" variables
+ ${source}= Evaluate ${hosts}.get("src")
+ ${length_src}= Get Length ${source}
+ ${src}= Set Variable src
+ FOR ${INDEX} IN RANGE 0 ${length_src}
+ Set Suite Variable ${${src}${INDEX}} ${source[${INDEX}]}
+ END
+ ${destination}= Evaluate ${hosts}.get("dst")
+ ${length_dst}= Get Length ${destination}
+ ${dst}= Set Variable dst
+ FOR ${INDEX} IN RANGE 0 ${length_dst}
+ Set Suite Variable ${${dst}${INDEX}} ${destination[${INDEX}]}
+ END