diff options
69 files changed, 3761 insertions, 1987 deletions
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..fe258c6c6 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[report] +exclude_lines = + if __name__ == .__main__.: @@ -3,15 +3,16 @@ Project Creation Date: January 20, 2015 Project Category: Integration & Testing Lifecycle State: Incubation Primary Contact: Jose Lausuch (jose.lausuch@ericsson.com) -Project Lead: Jose lausuch (jose.lausuch@ericsson.com) +Project Lead: Jose lausuch (jose.lausuch@ericsson.com) Jira Project Name: Base System Functionality Testing Project Jira Project Prefix: FUNCTEST Mailing list tag: [functest] -IRC: Server:freenode.net Channel:#opnfv-testperf +IRC: Server:freenode.net Channel:#opnfv-functest Repository: functest Committers: -serena.feng.711@gmail.com +yaohelan@huawei.com +feng.xiaowei@zte.com.cn ollivier.cedric@gmail.com jose.lausuch@ericsson.com morgan.richomme@orange.com diff --git a/docker/Dockerfile b/docker/Dockerfile index f76dd7979..5105fbbd1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -32,7 +32,7 @@ LABEL version="0.1" description="OPNFV Functest Docker container" ARG BRANCH=master ARG TEMPEST_TAG=12.2.0 ARG RALLY_TAG=0.7.0 -ARG ODL_TAG=release/beryllium-sr3 +ARG ODL_TAG=release/beryllium-sr4 ARG OPENSTACK_TAG=stable/mitaka ARG KINGBIRD_TAG=0.2.2 ARG VIMS_TAG=stable @@ -125,7 +125,7 @@ RUN pip install -r ${REPOS_DIR}/rally/requirements.txt RUN pip install -r ${REPOS_DIR}/tempest/requirements.txt RUN find ${FUNCTEST_REPO_DIR} -name "*.py" \ - -not -path *unit_tests* |xargs grep __main__ |cut -d\: -f 1 |xargs chmod -c 755 \ + -not -path "*tests/unit*" |xargs grep __main__ |cut -d\: -f 1 |xargs chmod -c 755 \ && find ${FUNCTEST_REPO_DIR} -name "*.sh" |xargs grep \#\! |cut -d\: -f 1 |xargs chmod -c 755 RUN /bin/bash ${REPOS_DIR}/parser/tests/parser_install.sh ${REPOS_DIR} @@ -142,7 +142,13 @@ RUN curl -L https://get.rvm.io | bash -s stable RUN git clone --depth 1 https://gerrit.cablelabs.com/snaps-provisioning ${REPOS_DIR}/snaps RUN pip install -e ${REPOS_DIR}/snaps/ -RUN /bin/bash -c ". ${REPOS_DIR}/sfc/tests/functest/odl-sfc/tacker_client_install.sh" +# SFC integration +RUN /bin/bash -c ". ${REPOS_DIR}/sfc/sfc/tests/functest/setup_scripts/tacker_client_install.sh" +RUN cd ${REPOS_DIR}/sfc && pip install . + +# SDNVPN integration +RUN cd ${REPOS_DIR}/sdnvpn && pip install . + RUN cd ${REPOS_DIR}/bgpvpn && pip install . #RUN cd ${REPOS_DIR}/kingbird && pip install -e . RUN cd ${REPOS_DIR}/moon/moonclient/ && python setup.py install diff --git a/docker/docker_remote_api/docs/TLS-intro.rst b/docker/docker_remote_api/docs/TLS-intro.rst index 934f99a8b..44fdd4aed 100644 --- a/docker/docker_remote_api/docs/TLS-intro.rst +++ b/docker/docker_remote_api/docs/TLS-intro.rst @@ -1,107 +1,107 @@ -Encrypt the docker remote API via TLS for Ubuntu and CentOS
-
-[Introduction]
-The Docker daemon can listen to Docker Remote API requests via three types of
-Socket: unix, tcp and fd. By default, a unix domain socket (or IPC socket) is
-created at /var/run/docker.sock, requiring either root permission, or docker
-group membership.
-
-Port 2375 is conventionally used for un-encrypted communition with Docker daemon
-remotely, where docker server can be accessed by any docker client via tcp socket
-in local area network. You can listen to port 2375 on all network interfaces with
--H tcp://0.0.0.0:2375, where 0.0.0.0 means any available IP address on host, and
-tcp://0.0.0.0:2375 indicates that port 2375 is listened on any IP of daemon host.
-If we want to make docker server open on the Internet via TCP port, and only trusted
-clients have the right to access the docker server in a safe manner, port 2376 for
-encrypted communication with the daemon should be listened. It can be achieved to
-create certificate and distribute it to the trusted clients.
-
-Through creating self-signed certificate, and using --tlsverify command when running
-Docker daemon, Docker daemon opens the TLS authentication. Thus only the clients
-with related private key files can have access to the Docker daemon's server. As
-long as the key files for encryption are secure between docker server and client,
-the Docker daemon can keep secure.
-In summary,
-Firstly we should create docker server certificate and related key files, which
-are distributed to the trusted clients.
-Then the clients with related key files can access docker server.
-
-[Steps]
-1.0. Create a CA, server and client keys with OpenSSL.
- OpenSSL is used to generate certificate, and can be installed as follows.
- apt-get install openssl openssl-devel
-
-1.1 First generate CA private and public keys.
- openssl genrsa -aes256 -out ca-key.pem 4096
- openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
-
- You are about to be asked to enter information that will be incorporated
- into your certificate request, where the instance of $HOST should be replaced
- with the DNS name of your Docker daemon's host, here the DNS name of my Docker
- daemon is ly.
- Common Name (e.g. server FQDN or YOUR name) []:$HOST
-
-1.2 Now we have a CA (ca-key.pem and ca.pem), you can create a server key and
-certificate signing request.
- openssl genrsa -out server-key.pem 4096
- openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
-
-1.3 Sign the public key with our CA.
- TLS connections can be made via IP address as well as DNS name, they need to be
- specified when creating the certificate.
-
- echo subjectAltName = IP:172.16.10.121,IP:127.0.0.1 > extfile.cnf
- openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
- -CAcreateserial -out server-cert.pem -extfile extfile.cnf
-
-1.4 For client authentication, create a client key and certificate signing request.
- openssl genrsa -out key.pem 4096
- openssl req -subj '/CN=client' -new -key key.pem -out client.csr
-
-1.5 To make the key suitable for client authentication, create an extensions config file.
- echo extendedKeyUsage = clientAuth > extfile.cnf
-
-1.6 Sign the public key and after generating cert.pem and server-cert.pem, two certificate
- signing requests can be removed.
- openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
- -CAcreateserial -out cert.pem -extfile extfile.cnf
-
-1.7 In order to protect your keys from accidental damage, you may change file modes to
- be only readable.
- chmod -v 0400 ca-key.pem key.pem server-key.pem
- chmod -v 0444 ca.pem server-cert.pem cert.pem
-
-1.8 Build docker server
- dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
- -H=0.0.0.0:2376
- Then, it can be seen from the command 'netstat -ntlp' that port 2376 has been listened
- and the Docker daemon only accept connections from clients providing a certificate
- trusted by our CA.
-
-1.9 Distribute the keys to the client
- scp /etc/docker/ca.pem wwl@172.16.10.121:/etc/docker
- scp /etc/docker/cert.pem wwl@172.16.10.121:/etc/docker
- scp /etc/docker/key.pem wwl@172.16.10.121:/etc/docker
- Where, wwl and 172.16.10.121 is the username and IP of the client respectively.
- And the password of the client is needed when you distribute the keys to the client.
-
-1.10 To access Docker daemon from the client via keys.
- docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
- -H=$HOST:2376 version
-
- Then we can operate docker in the Docker daemon from the client vis keys, for example:
- 1) create container from the client
- docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 run -d \
- -it --name w1 grafana/grafana
- 2) list containers from the client
- docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 pa -a
- 3) stop/start containers from the client
- docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 stop w1
- docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 start w1
-
-
-
-
-
-
-
+Encrypt the docker remote API via TLS for Ubuntu and CentOS + +[Introduction] +The Docker daemon can listen to Docker Remote API requests via three types of +Socket: unix, tcp and fd. By default, a unix domain socket (or IPC socket) is +created at /var/run/docker.sock, requiring either root permission, or docker +group membership. + +Port 2375 is conventionally used for un-encrypted communition with Docker daemon +remotely, where docker server can be accessed by any docker client via tcp socket +in local area network. You can listen to port 2375 on all network interfaces with +-H tcp://0.0.0.0:2375, where 0.0.0.0 means any available IP address on host, and +tcp://0.0.0.0:2375 indicates that port 2375 is listened on any IP of daemon host. +If we want to make docker server open on the Internet via TCP port, and only trusted +clients have the right to access the docker server in a safe manner, port 2376 for +encrypted communication with the daemon should be listened. It can be achieved to +create certificate and distribute it to the trusted clients. + +Through creating self-signed certificate, and using --tlsverify command when running +Docker daemon, Docker daemon opens the TLS authentication. Thus only the clients +with related private key files can have access to the Docker daemon's server. As +long as the key files for encryption are secure between docker server and client, +the Docker daemon can keep secure. +In summary, +Firstly we should create docker server certificate and related key files, which +are distributed to the trusted clients. +Then the clients with related key files can access docker server. + +[Steps] +1.0. Create a CA, server and client keys with OpenSSL. + OpenSSL is used to generate certificate, and can be installed as follows. + apt-get install openssl openssl-devel + +1.1 First generate CA private and public keys. + openssl genrsa -aes256 -out ca-key.pem 4096 + openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem + + You are about to be asked to enter information that will be incorporated + into your certificate request, where the instance of $HOST should be replaced + with the DNS name of your Docker daemon's host, here the DNS name of my Docker + daemon is ly. + Common Name (e.g. server FQDN or YOUR name) []:$HOST + +1.2 Now we have a CA (ca-key.pem and ca.pem), you can create a server key and +certificate signing request. + openssl genrsa -out server-key.pem 4096 + openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr + +1.3 Sign the public key with our CA. + TLS connections can be made via IP address as well as DNS name, they need to be + specified when creating the certificate. + + echo subjectAltName = IP:172.16.10.121,IP:127.0.0.1 > extfile.cnf + openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \ + -CAcreateserial -out server-cert.pem -extfile extfile.cnf + +1.4 For client authentication, create a client key and certificate signing request. + openssl genrsa -out key.pem 4096 + openssl req -subj '/CN=client' -new -key key.pem -out client.csr + +1.5 To make the key suitable for client authentication, create an extensions config file. + echo extendedKeyUsage = clientAuth > extfile.cnf + +1.6 Sign the public key and after generating cert.pem and server-cert.pem, two certificate + signing requests can be removed. + openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \ + -CAcreateserial -out cert.pem -extfile extfile.cnf + +1.7 In order to protect your keys from accidental damage, you may change file modes to + be only readable. + chmod -v 0400 ca-key.pem key.pem server-key.pem + chmod -v 0444 ca.pem server-cert.pem cert.pem + +1.8 Build docker server + dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \ + -H=0.0.0.0:2376 + Then, it can be seen from the command 'netstat -ntlp' that port 2376 has been listened + and the Docker daemon only accept connections from clients providing a certificate + trusted by our CA. + +1.9 Distribute the keys to the client + scp /etc/docker/ca.pem wwl@172.16.10.121:/etc/docker + scp /etc/docker/cert.pem wwl@172.16.10.121:/etc/docker + scp /etc/docker/key.pem wwl@172.16.10.121:/etc/docker + Where, wwl and 172.16.10.121 is the username and IP of the client respectively. + And the password of the client is needed when you distribute the keys to the client. + +1.10 To access Docker daemon from the client via keys. + docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \ + -H=$HOST:2376 version + + Then we can operate docker in the Docker daemon from the client vis keys, for example: + 1) create container from the client + docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 run -d \ + -it --name w1 grafana/grafana + 2) list containers from the client + docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 pa -a + 3) stop/start containers from the client + docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 stop w1 + docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=ly:2376 start w1 + + + + + + + diff --git a/docker/docker_remote_api/enable_remote_api.sh b/docker/docker_remote_api/enable_remote_api.sh index 6867eeddf..76e59b850 100644..100755 --- a/docker/docker_remote_api/enable_remote_api.sh +++ b/docker/docker_remote_api/enable_remote_api.sh @@ -1,51 +1,51 @@ -#!/bin/bash
-# SPDX-license-identifier: Apache-2.0
-
-# ******************************
-# Script to update the docker host configuration
-# to enable Docker Remote API
-# ******************************
-
-if [ -f /etc/lsb-release ]; then
- #tested on ubuntu 14.04 and 16.04
- if grep -q "#DOCKER_OPTS=" "/etc/default/docker"; then
- cp /etc/default/docker /etc/default/docker.bak
- sed -i 's/^#DOCKER_OPTS.*$/DOCKER_OPTS=\"-H unix:\/\/\/var\/run\/docker.sock -H tcp:\/\/0.0.0.0:2375\"/g' /etc/default/docker
- else
- echo DOCKER_OPTS=\"-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375\" >> /etc/default/docker
- fi
- service docker restart
- #docker start $(docker ps -aq)
-elif [ -f /etc/system-release ]; then
- #tested on centos 7.2
- if grep -q "ExecStart=\/usr\/bin\/docker-current daemon" "/lib/systemd/system/docker.service"; then
- cp /lib/systemd/system/docker.service /lib/systemd/system/docker.service.bak
- sed -i 's/^ExecStart=.*$/ExecStart=\/usr\/bin\/docker daemon -H tcp:\/\/0.0.0.0:2375 -H unix:\/\/\/var\/run\/docker.sock \\/g' /lib/systemd/system/docker.service
- systemctl daemon-reload
- systemctl restart docker
- else
- echo "to be implemented"
- fi
-else
- echo "OS is not supported"
-fi
-
-# Issue Note for Ubuntu
-# 1. If the configuration of the file /etc/default/docker does not take effect after restarting docker service,
-# you may try to modify /lib/systemd/system/docker.service
-# commands:
-# cp /lib/systemd/system/docker.service /lib/systemd/system/docker.service.bak
-# sed -i '/^ExecStart/i\EnvironmentFile=-/etc/default/docker' /lib/systemd/system/docker.service
-# sed -i '/ExecStart=\/usr\/bin\/dockerd/{;s/$/ \$DOCKER_OPTS/}' /lib/systemd/system/docker.service
-# systemctl daemon-reload
-# service docker restart
-# 2. Systemd is a system and session manager for Linux, where systemctl is one tool for systemd to view and control systemd.
-# If the file /lib/systemd/system/docker.service is modified, systemd has to be reloaded to scan new or changed units.
-# 1) systemd and related packages are available on the PPA. To use the PPA, first add it to your software sources list as follows.
-# add-apt-repository ppa:pitti/systemd
-# apt-get update
-# 2) system can be installed from the PPS as follows.
-# apt-get install systemd libpam-systemd systemd-ui
-
-
-
+#!/bin/bash +# SPDX-license-identifier: Apache-2.0 + +# ****************************** +# Script to update the docker host configuration +# to enable Docker Remote API +# ****************************** + +if [ -f /etc/lsb-release ]; then + #tested on ubuntu 14.04 and 16.04 + if grep -q "#DOCKER_OPTS=" "/etc/default/docker"; then + cp /etc/default/docker /etc/default/docker.bak + sed -i 's/^#DOCKER_OPTS.*$/DOCKER_OPTS=\"-H unix:\/\/\/var\/run\/docker.sock -H tcp:\/\/0.0.0.0:2375\"/g' /etc/default/docker + else + echo DOCKER_OPTS=\"-H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375\" >> /etc/default/docker + fi + service docker restart + #docker start $(docker ps -aq) +elif [ -f /etc/system-release ]; then + #tested on centos 7.2 + if grep -q "ExecStart=\/usr\/bin\/docker-current daemon" "/lib/systemd/system/docker.service"; then + cp /lib/systemd/system/docker.service /lib/systemd/system/docker.service.bak + sed -i 's/^ExecStart=.*$/ExecStart=\/usr\/bin\/docker daemon -H tcp:\/\/0.0.0.0:2375 -H unix:\/\/\/var\/run\/docker.sock \\/g' /lib/systemd/system/docker.service + systemctl daemon-reload + systemctl restart docker + else + echo "to be implemented" + fi +else + echo "OS is not supported" +fi + +# Issue Note for Ubuntu +# 1. If the configuration of the file /etc/default/docker does not take effect after restarting docker service, +# you may try to modify /lib/systemd/system/docker.service +# commands: +# cp /lib/systemd/system/docker.service /lib/systemd/system/docker.service.bak +# sed -i '/^ExecStart/i\EnvironmentFile=-/etc/default/docker' /lib/systemd/system/docker.service +# sed -i '/ExecStart=\/usr\/bin\/dockerd/{;s/$/ \$DOCKER_OPTS/}' /lib/systemd/system/docker.service +# systemctl daemon-reload +# service docker restart +# 2. Systemd is a system and session manager for Linux, where systemctl is one tool for systemd to view and control systemd. +# If the file /lib/systemd/system/docker.service is modified, systemd has to be reloaded to scan new or changed units. +# 1) systemd and related packages are available on the PPA. To use the PPA, first add it to your software sources list as follows. +# add-apt-repository ppa:pitti/systemd +# apt-get update +# 2) system can be installed from the PPS as follows. +# apt-get install systemd libpam-systemd systemd-ui + + + diff --git a/docs/internship/security_group/index.rst b/docs/internship/security_group/index.rst new file mode 100644 index 000000000..d1cdbdd8f --- /dev/null +++ b/docs/internship/security_group/index.rst @@ -0,0 +1,70 @@ +======= +License +======= + +Functest Docs are licensed under a Creative Commons Attribution 4.0 +International License. +You should have received a copy of the license along with this. +If not, see <http://creativecommons.org/licenses/by/4.0/>. + +================================== +Functest Security group test cases +================================== + +Author: Girish Sukhatankar +mentors: D.Blaisonneau, J.Lausuch, M.Richomme + +Abstract +======== + + +Version history +=============== + ++------------+----------+------------------+------------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++------------+----------+------------------+------------------------+ +| 2016-??-?? | 0.0.1 | Morgan Richomme | Beginning of the | +| | | (Orange) | Internship | ++------------+----------+------------------+------------------------+ + + +Overview: +========= + + + + +Problem Statement: +------------------ + + + +Curation Phase +-------------- + + + + + +Schedule: +========= + + + ++--------------------------+------------------------------------------+ +| **Date** | **Comment** | +| | | ++--------------------------+------------------------------------------+ +| December - January | ........ | ++--------------------------+------------------------------------------+ +| January - february | ........ | ++--------------------------+------------------------------------------+ + + +References: +=========== + +.. _`[1]` : https://wiki.opnfv.org/display/DEV/Intern+Project%3A+Security+groups+test+case+in+Functest + diff --git a/docs/internship/testapi_evolution/index.rst b/docs/internship/testapi_evolution/index.rst new file mode 100644 index 000000000..f2583e2f0 --- /dev/null +++ b/docs/internship/testapi_evolution/index.rst @@ -0,0 +1,70 @@ +======= +License +======= + +Functest Docs are licensed under a Creative Commons Attribution 4.0 +International License. +You should have received a copy of the license along with this. +If not, see <http://creativecommons.org/licenses/by/4.0/>. + +================== +Test API evolution +================== + +Author: Rohit Sakala +Mentors: S. Feng, J.Lausuch, M.Richomme + +Abstract +======== + + +Version history +=============== + ++------------+----------+------------------+------------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++------------+----------+------------------+------------------------+ +| 2016-??-?? | 0.0.1 | Morgan Richomme | Beginning of the | +| | | (Orange) | Internship | ++------------+----------+------------------+------------------------+ + + +Overview: +========= + + + + +Problem Statement: +------------------ + + + +Curation Phase +-------------- + + + + + +Schedule: +========= + + + ++--------------------------+------------------------------------------+ +| **Date** | **Comment** | +| | | ++--------------------------+------------------------------------------+ +| December - January | ........ | ++--------------------------+------------------------------------------+ +| January - february | ........ | ++--------------------------+------------------------------------------+ + + +References: +=========== + +.. _`[1]` : https://wiki.opnfv.org/display/DEV/Intern+Project%3A+testapi+evolution + diff --git a/docs/internship/unit_tests/index.rst b/docs/internship/unit_tests/index.rst new file mode 100644 index 000000000..f969aa72d --- /dev/null +++ b/docs/internship/unit_tests/index.rst @@ -0,0 +1,70 @@ +======= +License +======= + +Functest Docs are licensed under a Creative Commons Attribution 4.0 +International License. +You should have received a copy of the license along with this. +If not, see <http://creativecommons.org/licenses/by/4.0/>. + +=================== +Functest Unit tests +=================== + +Author: Ashish Kumar +Mentors: H.Yao, J.Lausuch, M.Richomme + +Abstract +======== + + +Version history +=============== + ++------------+----------+------------------+------------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++------------+----------+------------------+------------------------+ +| 2016-??-?? | 0.0.1 | Morgan Richomme | Beginning of the | +| | | (Orange) | Internship | ++------------+----------+------------------+------------------------+ + + +Overview: +========= + + + + +Problem Statement: +------------------ + + + +Curation Phase +-------------- + + + + + +Schedule: +========= + + + ++--------------------------+------------------------------------------+ +| **Date** | **Comment** | +| | | ++--------------------------+------------------------------------------+ +| December - January | ........ | ++--------------------------+------------------------------------------+ +| January - february | ........ | ++--------------------------+------------------------------------------+ + + +References: +=========== + +.. _`[1]` : https://wiki.opnfv.org/display/DEV/Intern+Project%3A+Functest+unit+tests + diff --git a/docs/internship/vnf_catalog/index.rst b/docs/internship/vnf_catalog/index.rst new file mode 100644 index 000000000..df7633391 --- /dev/null +++ b/docs/internship/vnf_catalog/index.rst @@ -0,0 +1,170 @@ +======= +License +======= + +Functest Docs are licensed under a Creative Commons Attribution 4.0 +International License. +You should have received a copy of the license along with this. +If not, see <http://creativecommons.org/licenses/by/4.0/>. + +======================= +Open Source VNF Catalog +======================= + +Author: Kumar Rishabh +Mentors: B.Souville, M.Richomme, J.Lausuch + +Abstract +======== + + + +Version hissory +=============== + ++------------+----------+------------------+------------------------+ +| **Date** | **Ver.** | **Author** | **Comment** | +| | | | | ++------------+----------+------------------+------------------------+ +| 2016-12-12 | 0.0.1 | Morgan Richomme | Beginning of the | +| | | (Orange) | Internship | ++------------+----------+------------------+------------------------+ + + +Overview: +========= + + +This project aims to create an Open Source catalog for reference and +classification of Virtual Network Functions (VNFs)s available on +Internet. The classification method proposed will be in sync with the +requirements of Telcos active in NFV landscape. The project aims to have +running web platform similar to [1] by the mid of internship (2nd week +of March). By the penultimate month of internship I aim to have fully +functional implementation of an Open Source VNF in functest. + + +Problem Statement: +------------------ + +OPNFV aims to be the reference platform for development, +standardization and integration of Open Source NFV components across +various Open Source Platforms. It mainly deals with the infrastructure +through the Network Function Virtualization Infrastructure (NFVI) and +Virtual Infrastructure manager (VIM). The MANO (Management and +orchestration) stacks have been introduced recently. VNFs are not +directly in OPNFV scope, however VNFs are needed to test and qualify the +infrastructure. In this regard having a common curated Open Source +Reference VNF catalog would be of immense importance to community. + +Since major focus of OPNFV is Telcos, a curated platform targeted from +industry point of view would be very useful. We plan to divide the +entire project into three major phases(with some iterative improvements +and overlaps) + + +Curation Phase +-------------- +This phase pertains to studying various Open Source VNFs available and +classification of them based on certain parameters. The parameters that +I currently have in mind are: + * Developer Metrics: These pertain to repo characteristics of VNF under + study + * Usage Statistics - Activity, Number of Commits, stars + * Maturity Statistics - For instance if an NFV community decides code + coverage is important for them, it shows the NFV community is serious + about taking the project forward + * Technical Tagging: These are the tags that pertain to technical + characteristics of a VNF + * Broad Use Cases - Whether the VNF fits strictly in IaaS, PaaS or + SaaS layer or is an hybrid of two/all. + * Generic Use Cases - This in my opinion is the broadest + classification category. For instance a VNF could be built with a + broad idea of powering IOT devices at home or from usage perspective + of Telco Operators (vFW, vEPC, vIMS, vCDN, vAAA, vCPE,...).`[2]`_ + * Fields of Application + * Library Status - Whether APIs are standardized, support RESTful + services. + * Dependency Forwarding Graph - This is pretty complex tagging + mechanism. It essentially tries to establish a graph relationship + between the VNFs (elementary VNFs are used in Service Function + Chaining chains such as Firewall, DPI, content enrichment,..). In my + opinion this is useful immensely. This will allow users to go to + platform and ask a question like - “I have this X tech stack to + support, Y and Z are my use cases, which NFVs should I use to support + this. + * Visitor Score - Based on `[1]`_ I plan to evolve a visitor score for + the platform. This will allow users to score an NFV on certain + parameters, may be post comments. + +**I plan to use the above three scores and evolve cumulative score which +will be displayed next to each of the NFV on the platform.** + + * Platform building phase - This will involve erecting a Web Platform + which will be similar to this `[1]`_. I am decently familiar with + Django and hence I will write the platform in Django. There are two + action plans that I have in mind right now. Either I can start writing + the platform simultaneously which will help keep track of my progress + or I can write the platform after 1.5 - 2 months into the internship. + Either way I aim to have the Web Platform ready by March 12. + + * Functest VNF implementation phase - This is the last phase that will + involve writing a fully functional implementation of an Open Source VNF + into Functest. I will undertake this after I am 3 months into the + internship. I have a decent familiarity with python and hence I think + it shouldn’t be too difficult. I need to decide how complex the VNFI + should undertake this exercise for (e.g. AAA such as free radius sounds + relatively easy, vCDN is much more challenging). + This will be decided in consent with my mentors. + + + + +Schedule: +========= +I plan to take this project in 6 months time frame as I want to use it +as a chance to read more about NFVs in particular and SDN in general + + ++--------------------------+------------------------------------------+ +| **Date** | **Comment** | +| | | ++--------------------------+------------------------------------------+ +| December 12 - January 12 | Study the above mentioned metrics | +| | Decide which of them are important for | +| | community (and which are not). | ++--------------------------+------------------------------------------+ +| January 12 - January 27 | Make a database for the above studied | +| | metrics and evolve it further based on | +| | Mentors’ input. + associated API | ++--------------------------+------------------------------------------+ +| January 27 - February 5 | Compile the data collected above and make| +| | it public. Although I can keep everything| +| | public from the beginning too. My | +| | rationale of not making the entire data | +| | public in initial stage as the errors | +| | caused by me could be misleading for | +| | developers. | ++--------------------------+------------------------------------------+ +| February 5 - March 5 | Erect the Web Platform and release it | +| | for restricted group for alpha testing. | ++--------------------------+------------------------------------------+ +| March 5 - March 12 | Make it public. Release it to public for | +| | beta testing. Fix Bugs. | ++--------------------------+------------------------------------------+ +| March 12 - April 12 | Start working on implementation of an | +| | Open Source VNF in Functest. | ++--------------------------+------------------------------------------+ +| April 12 - May 12 | I will decided what to do here based on | +| | discussion with mentors. | ++--------------------------+------------------------------------------+ + + +References: +=========== + +.. _`[1]` : Openhub: https://www.openhub.net/explore/projects + +.. _`[2]` : ETSI NFV White Paper: https://portal.etsi.org/Portals/0/TBpages/NFV/Docs/NFV_White_Paper3.pdf + +.. _`[3]` : https://wiki.opnfv.org/display/DEV/Intern+Project%3A+Open+Source+VNF+catalog diff --git a/functest/ci/config_functest.yaml b/functest/ci/config_functest.yaml index 0da2bb8f3..15e0d3a1a 100755 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -1,211 +1,211 @@ -general:
- directories:
- # Relative to the path where the repo is cloned:
- dir_vping: functest/opnfv_tests/openstack/vping
- dir_odl: functest/opnfv_tests/sdn/odl
- dir_rally: functest/opnfv_tests/openstack/rally
- dir_tempest_cases: functest/opnfv_tests/openstack/tempest/custom_tests
- dir_vIMS: functest/opnfv_tests/vnf/ims
- dir_onos: functest/opnfv_tests/sdn/onos/teston
- dir_onos_sfc: functest/opnfv_tests/sdn/onos/sfc
-
- # Absolute path
- dir_home: /home/opnfv
- dir_repos: /home/opnfv/repos
- dir_repo_functest: /home/opnfv/repos/functest
- dir_repo_rally: /home/opnfv/repos/rally
- dir_repo_tempest: /home/opnfv/repos/tempest
- dir_repo_releng: /home/opnfv/repos/releng
- dir_repo_vims_test: /home/opnfv/repos/vims-test
- dir_repo_sdnvpn: /home/opnfv/repos/sdnvpn
- dir_repo_sfc: /home/opnfv/repos/sfc
- dir_repo_onos: /home/opnfv/repos/onos
- dir_repo_promise: /home/opnfv/repos/promise
- dir_repo_doctor: /home/opnfv/repos/doctor
- dir_repo_copper: /home/opnfv/repos/copper
- dir_repo_ovno: /home/opnfv/repos/ovno
- dir_repo_parser: /home/opnfv/repos/parser
- dir_repo_domino: /home/opnfv/repos/domino
- dir_repo_snaps: /home/opnfv/repos/snaps
- dir_functest: /home/opnfv/functest
- dir_functest_test: /home/opnfv/repos/functest/functest/opnfv_tests
- dir_results: /home/opnfv/functest/results
- dir_functest_conf: /home/opnfv/functest/conf
- dir_functest_data: /home/opnfv/functest/data
- dir_vIMS_data: /home/opnfv/functest/data/vIMS/
- dir_rally_inst: /home/opnfv/.rally
-
- openstack:
- creds: /home/opnfv/functest/conf/openstack.creds
- snapshot_file: /home/opnfv/functest/conf/openstack_snapshot.yaml
-
- image_name: Cirros-0.3.4
- image_file_name: cirros-0.3.4-x86_64-disk.img
- image_disk_format: qcow2
-
- flavor_name: opnfv_flavor
- flavor_ram: 512
- flavor_disk: 1
- flavor_vcpus: 1
-
- # Private network for functest. Will be created by config_functest.py
- neutron_private_net_name: functest-net
- neutron_private_subnet_name: functest-subnet
- neutron_private_subnet_cidr: 192.168.120.0/24
- neutron_private_subnet_start: 192.168.120.2
- neutron_private_subnet_end: 192.168.120.254
- neutron_private_subnet_gateway: 192.168.120.254
- neutron_router_name: functest-router
-
- functest:
- testcases_yaml: /home/opnfv/repos/functest/functest/ci/testcases.yaml
-
-healthcheck:
- disk_image: /home/opnfv/functest/data/cirros-0.3.4-x86_64-disk.img
- disk_format: qcow2
- wait_time: 60
-
-snaps:
- use_keystone: True
- use_floating_ips: False
-
-vping:
- ping_timeout: 200
- vm_flavor: m1.tiny # adapt to your environment
- vm_name_1: opnfv-vping-1
- vm_name_2: opnfv-vping-2
- image_name: functest-vping
- vping_private_net_name: vping-net
- vping_private_subnet_name: vping-subnet
- vping_private_subnet_cidr: 192.168.130.0/24
- vping_router_name: vping-router
- vping_sg_name: vPing-sg
- vping_sg_descr: Security group for vPing test case
-
-onos_sfc:
- image_base_url: http://artifacts.opnfv.org/sfc/demo
- image_name: TestSfcVm
- image_file_name: firewall_block_image.img
-
-tempest:
- identity:
- tenant_name: tempest
- tenant_description: Tenant for Tempest test suite
- user_name: tempest
- user_password: tempest
- validation:
- ssh_timeout: 130
- private_net_name: tempest-net
- private_subnet_name: tempest-subnet
- private_subnet_cidr: 192.168.150.0/24
- router_name: tempest-router
- use_custom_images: False
- use_custom_flavors: False
-
-rally:
- deployment_name: opnfv-rally
- network_name: rally-net
- subnet_name: rally-subnet
- subnet_cidr: 192.168.140.0/24
- router_name: rally-router
-
-vIMS:
- general:
- tenant_name: vIMS
- tenant_description: vIMS Functionality Testing
- images:
- ubuntu:
- image_url: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img
- image_name: ubuntu_14.04
- centos:
- image_url: http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1510.qcow2
- image_name: centos_7
- cloudify:
- blueprint:
- url: https://github.com/boucherv-orange/cloudify-manager-blueprints.git
- branch: "3.3.1-build"
- requierments:
- ram_min: 3000
- os_image: centos_7
- inputs:
- keystone_username: ""
- keystone_password: ""
- keystone_tenant_name: ""
- keystone_url: ""
- manager_public_key_name: 'manager-kp'
- agent_public_key_name: 'agent-kp'
- image_id: ""
- flavor_id: "3"
- external_network_name: ""
- ssh_user: centos
- agents_user: ubuntu
- clearwater:
- blueprint:
- file_name: 'openstack-blueprint.yaml'
- name: "clearwater-opnfv"
- destination_folder: "opnfv-cloudify-clearwater"
- url: https://github.com/Orange-OpenSource/opnfv-cloudify-clearwater.git
- branch: "stable"
- deployment-name: 'clearwater-opnfv'
- requierments:
- ram_min: 1700
- os_image: ubuntu_14.04
- inputs:
- image_id: ''
- flavor_id: ''
- agent_user: 'ubuntu'
- external_network_name: ''
- public_domain: clearwater.opnfv
-ONOS:
- general:
- onosbench_username: 'root'
- onosbench_password: 'root'
- onoscli_username: 'root'
- onoscli_password: 'root'
- runtimeout: 300
- environment:
- OCT: '10.20.0.1'
- OC1: '10.20.0.7'
- OC2: '10.20.0.7'
- OC3: '10.20.0.7'
- OCN: '10.20.0.4'
- OCN2: '10.20.0.5'
- installer_master: '10.20.0.2'
- installer_master_username: 'root'
- installer_master_password: 'r00tme'
-multisite:
- fuel_environment:
- installer_username: 'root'
- installer_password: 'r00tme'
- compass_environment:
- installer_username: 'root'
- installer_password: 'root'
- multisite_controller_ip: '10.1.0.50'
-promise:
- tenant_name: promise
- tenant_description: promise Functionality Testing
- user_name: promiser
- user_pwd: test
- image_name: promise-img
- flavor_name: promise-flavor
- flavor_vcpus: 1
- flavor_ram: 128
- flavor_disk: 0
- network_name: promise-net
- subnet_name: promise-subnet
- subnet_cidr: 192.168.121.0/24
- router_name: promise-router
-
-example:
- example_vm_name: example-vm
- example_flavor: m1.small
- example_image_name: functest-example-vm
- example_private_net_name: example-net
- example_private_subnet_name: example-subnet
- example_private_subnet_cidr: 192.168.170.0/24
- example_router_name: example-router
- example_sg_name: example-sg
- example_sg_descr: Example Security group
-
-results:
- test_db_url: http://testresults.opnfv.org/test/api/v1
+general: + dir: + # Relative to the path where the repo is cloned: + vping: functest/opnfv_tests/openstack/vping + dir_odl: functest/opnfv_tests/sdn/odl + rally: functest/opnfv_tests/openstack/rally + tempest_cases: functest/opnfv_tests/openstack/tempest/custom_tests + dir_vIMS: functest/opnfv_tests/vnf/ims + dir_onos: functest/opnfv_tests/sdn/onos/teston + dir_onos_sfc: functest/opnfv_tests/sdn/onos/sfc + + # Absolute path + home: /home/opnfv + repos: /home/opnfv/repos + repo_functest: /home/opnfv/repos/functest + dir_repo_rally: /home/opnfv/repos/rally + repo_tempest: /home/opnfv/repos/tempest + dir_repo_releng: /home/opnfv/repos/releng + dir_repo_vims_test: /home/opnfv/repos/vims-test + repo_sdnvpn: /home/opnfv/repos/sdnvpn + repo_sfc: /home/opnfv/repos/sfc + dir_repo_onos: /home/opnfv/repos/onos + dir_repo_promise: /home/opnfv/repos/promise + dir_repo_doctor: /home/opnfv/repos/doctor + repo_copper: /home/opnfv/repos/copper + dir_repo_ovno: /home/opnfv/repos/ovno + repo_parser: /home/opnfv/repos/parser + repo_domino: /home/opnfv/repos/domino + repo_snaps: /home/opnfv/repos/snaps + functest: /home/opnfv/functest + functest_test: /home/opnfv/repos/functest/functest/opnfv_tests + results: /home/opnfv/functest/results + functest_conf: /home/opnfv/functest/conf + functest_data: /home/opnfv/functest/data + dir_vIMS_data: /home/opnfv/functest/data/vIMS/ + rally_inst: /home/opnfv/.rally + + openstack: + creds: /home/opnfv/functest/conf/openstack.creds + snapshot_file: /home/opnfv/functest/conf/openstack_snapshot.yaml + + image_name: Cirros-0.3.4 + image_file_name: cirros-0.3.4-x86_64-disk.img + image_disk_format: qcow2 + + flavor_name: opnfv_flavor + flavor_ram: 512 + flavor_disk: 1 + flavor_vcpus: 1 + + # Private network for functest. Will be created by config_functest.py + neutron_private_net_name: functest-net + neutron_private_subnet_name: functest-subnet + neutron_private_subnet_cidr: 192.168.120.0/24 + neutron_private_subnet_start: 192.168.120.2 + neutron_private_subnet_end: 192.168.120.254 + neutron_private_subnet_gateway: 192.168.120.254 + neutron_router_name: functest-router + + functest: + testcases_yaml: /home/opnfv/repos/functest/functest/ci/testcases.yaml + +healthcheck: + disk_image: /home/opnfv/functest/data/cirros-0.3.4-x86_64-disk.img + disk_format: qcow2 + wait_time: 60 + +snaps: + use_keystone: True + use_floating_ips: False + +vping: + ping_timeout: 200 + vm_flavor: m1.tiny # adapt to your environment + vm_name_1: opnfv-vping-1 + vm_name_2: opnfv-vping-2 + image_name: functest-vping + private_net_name: vping-net + private_subnet_name: vping-subnet + private_subnet_cidr: 192.168.130.0/24 + router_name: vping-router + sg_name: vPing-sg + sg_desc: Security group for vPing test case + +onos_sfc: + image_base_url: http://artifacts.opnfv.org/sfc/demo + image_name: TestSfcVm + image_file_name: firewall_block_image.img + +tempest: + identity: + tenant_name: tempest + tenant_description: Tenant for Tempest test suite + user_name: tempest + user_password: tempest + validation: + ssh_timeout: 130 + private_net_name: tempest-net + private_subnet_name: tempest-subnet + private_subnet_cidr: 192.168.150.0/24 + router_name: tempest-router + use_custom_images: False + use_custom_flavors: False + +rally: + deployment_name: opnfv-rally + network_name: rally-net + subnet_name: rally-subnet + subnet_cidr: 192.168.140.0/24 + router_name: rally-router + +vIMS: + general: + tenant_name: vIMS + tenant_description: vIMS Functionality Testing + images: + ubuntu: + image_url: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img + image_name: ubuntu_14.04 + centos: + image_url: http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1510.qcow2 + image_name: centos_7 + cloudify: + blueprint: + url: https://github.com/boucherv-orange/cloudify-manager-blueprints.git + branch: "3.3.1-build" + requierments: + ram_min: 3000 + os_image: centos_7 + inputs: + keystone_username: "" + keystone_password: "" + keystone_tenant_name: "" + keystone_url: "" + manager_public_key_name: 'manager-kp' + agent_public_key_name: 'agent-kp' + image_id: "" + flavor_id: "3" + external_network_name: "" + ssh_user: centos + agents_user: ubuntu + clearwater: + blueprint: + file_name: 'openstack-blueprint.yaml' + name: "clearwater-opnfv" + destination_folder: "opnfv-cloudify-clearwater" + url: https://github.com/Orange-OpenSource/opnfv-cloudify-clearwater.git + branch: "stable" + deployment-name: 'clearwater-opnfv' + requierments: + ram_min: 1700 + os_image: ubuntu_14.04 + inputs: + image_id: '' + flavor_id: '' + agent_user: 'ubuntu' + external_network_name: '' + public_domain: clearwater.opnfv +ONOS: + general: + onosbench_username: 'root' + onosbench_password: 'root' + onoscli_username: 'root' + onoscli_password: 'root' + runtimeout: 300 + environment: + OCT: '10.20.0.1' + OC1: '10.20.0.7' + OC2: '10.20.0.7' + OC3: '10.20.0.7' + OCN: '10.20.0.4' + OCN2: '10.20.0.5' + installer_master: '10.20.0.2' + installer_master_username: 'root' + installer_master_password: 'r00tme' +multisite: + fuel: + installer_username: 'root' + installer_password: 'r00tme' + compass: + installer_username: 'root' + installer_password: 'root' + multisite_controller_ip: '10.1.0.50' +promise: + tenant_name: promise + tenant_description: promise Functionality Testing + user_name: promiser + user_pwd: test + image_name: promise-img + flavor_name: promise-flavor + flavor_vcpus: 1 + flavor_ram: 128 + flavor_disk: 0 + network_name: promise-net + subnet_name: promise-subnet + subnet_cidr: 192.168.121.0/24 + router_name: promise-router + +example: + vm_name: example-vm + flavor: m1.small + image_name: functest-example-vm + private_net_name: example-net + private_subnet_name: example-subnet + private_subnet_cidr: 192.168.170.0/24 + router_name: example-router + sg_name: example-sg + sg_desc: Example Security group + +results: + test_db_url: http://testresults.opnfv.org/test/api/v1 diff --git a/functest/ci/exec_test.sh b/functest/ci/exec_test.sh index 913ce08ec..109de078f 100755 --- a/functest/ci/exec_test.sh +++ b/functest/ci/exec_test.sh @@ -61,17 +61,7 @@ function odl_tests(){ fi } -function sfc_prepare(){ - ids=($(neutron security-group-list|grep default|awk '{print $2}')) - for id in ${ids[@]}; do - if ! neutron security-group-show $id|grep "22/tcp" &>/dev/null; then - neutron security-group-rule-create --protocol tcp \ - --port-range-min 22 --port-range-max 22 --direction ingress $id - neutron security-group-rule-create --protocol tcp \ - --port-range-min 22 --port-range-max 22 --direction egress $id - fi - done -} + function run_test(){ test_name=$1 @@ -93,14 +83,6 @@ function run_test(){ --ospassword ${OS_PASSWORD} \ --odlip $odl_ip --odlwebport $odl_port ${args} ;; - "tempest_smoke_serial") - python ${FUNCTEST_TEST_DIR}/openstack/tempest/run_tempest.py \ - $clean_flag -s -m smoke $report - ;; - "tempest_full_parallel") - python ${FUNCTEST_TEST_DIR}/openstack/tempest/run_tempest.py \ - $serial_flag $clean_flag -m full $report - ;; "vims") python ${FUNCTEST_TEST_DIR}/vnf/ims/vims.py $clean_flag $report ;; @@ -132,7 +114,7 @@ function run_test(){ "security_scan") echo "Sourcing Credentials ${FUNCTEST_CONF_DIR}/stackrc for undercloud .." source ${FUNCTEST_CONF_DIR}/stackrc - python ${REPOS_DIR}/securityscanning/security_scan.py --config ${REPOS_DIR}/securityscanning/config.ini + python ${FUNCTEST_TEST_DIR}/security_scan/security_scan.py --config ${FUNCTEST_TEST_DIR}/security_scan/config.ini ;; "copper") python ${FUNCTEST_TEST_DIR}/features/copper.py $report @@ -140,19 +122,6 @@ function run_test(){ "moon") python ${REPOS_DIR}/moon/tests/run_tests.py $report ;; - "multisite") - python ${FUNCTEST_TEST_DIR}/openstack/tempest/gen_tempest_conf.py - python ${FUNCTEST_TEST_DIR}/openstack/tempest/run_tempest.py \ - $clean_flag -s -m feature_multisite $report \ - -c ${FUNCTEST_TEST_DIR}/openstack/tempest/tempest_multisite.conf - ;; - "odl-sfc") - ODL_SFC_DIR=${REPOS_DIR}/sfc/tests/functest/odl-sfc - # pass FUNCTEST_REPO_DIR inside prepare_odl_sfc.bash - FUNCTEST_REPO_DIR=${FUNCTEST_REPO_DIR} python ${ODL_SFC_DIR}/prepare_odl_sfc.py || exit $? - source ${ODL_SFC_DIR}/tackerc - python ${ODL_SFC_DIR}/sfc.py $report - ;; *) echo "The test case '${test_name}' does not exist." exit 1 @@ -197,10 +166,6 @@ done echo "Sourcing Credentials ${creds} to run the test.." source ${creds} -# ODL Boron workaround to create additional flow rules to allow port 22 TCP -if [[ $DEPLOY_SCENARIO == *"odl_l2-sfc"* ]]; then - sfc_prepare -fi # Run test run_test $TEST diff --git a/functest/ci/generate_report.py b/functest/ci/generate_report.py index a90bc5553..89d8fc628 100755 --- a/functest/ci/generate_report.py +++ b/functest/ci/generate_report.py @@ -1,11 +1,17 @@ +#!/usr/bin/env python +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# import json import re import urllib2 import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils -import functest.utils.functest_constants as ft_constants - +from functest.utils.constants import CONST COL_1_LEN = 25 COL_2_LEN = 15 @@ -17,14 +23,6 @@ COL_5_LEN = 75 # and then we can print the url to the specific test result -class GlobalVariables: - IS_CI_RUN = ft_constants.IS_CI_RUN - BUILD_TAG = ft_constants.CI_BUILD_TAG - INSTALLER = ft_constants.CI_INSTALLER_TYPE - CI_LOOP = ft_constants.CI_LOOP - SCENARIO = ft_constants.CI_SCENARIO - - logger = ft_logger.Logger("generate_report").getLogger() @@ -42,7 +40,7 @@ def init(tiers_to_run): def get_results_from_db(): url = "%s/results?build_tag=%s" % (ft_utils.get_db_url(), - GlobalVariables.BUILD_TAG) + CONST.BUILD_TAG) logger.debug("Query to rest api: %s" % url) try: data = json.load(urllib2.urlopen(url)) @@ -69,7 +67,7 @@ def print_line(w1, w2='', w3='', w4='', w5=''): '| ' + w2.ljust(COL_2_LEN - 1) + '| ' + w3.ljust(COL_3_LEN - 1) + '| ' + w4.ljust(COL_4_LEN - 1)) - if GlobalVariables.IS_CI_RUN: + if CONST.IS_CI_RUN: str += ('| ' + w5.ljust(COL_5_LEN - 1)) str += '|\n' return str @@ -77,7 +75,7 @@ def print_line(w1, w2='', w3='', w4='', w5=''): def print_line_no_columns(str): TOTAL_LEN = COL_1_LEN + COL_2_LEN + COL_3_LEN + COL_4_LEN + 2 - if GlobalVariables.IS_CI_RUN: + if CONST.IS_CI_RUN: TOTAL_LEN += COL_5_LEN + 1 return ('| ' + str.ljust(TOTAL_LEN) + "|\n") @@ -87,7 +85,7 @@ def print_separator(char="=", delimiter="+"): delimiter + char * COL_2_LEN + delimiter + char * COL_3_LEN + delimiter + char * COL_4_LEN) - if GlobalVariables.IS_CI_RUN: + if CONST.IS_CI_RUN: str += (delimiter + char * COL_5_LEN) str += '+\n' return str @@ -96,7 +94,7 @@ def print_separator(char="=", delimiter="+"): def main(args): executed_test_cases = args - if GlobalVariables.IS_CI_RUN: + if CONST.IS_CI_RUN: results = get_results_from_db() if results is not None: for test in executed_test_cases: @@ -105,15 +103,15 @@ def main(args): "result": data['result']}) TOTAL_LEN = COL_1_LEN + COL_2_LEN + COL_3_LEN + COL_4_LEN - if GlobalVariables.IS_CI_RUN: + if CONST.IS_CI_RUN: TOTAL_LEN += COL_5_LEN MID = TOTAL_LEN / 2 - if GlobalVariables.BUILD_TAG is not None: - if re.search("daily", GlobalVariables.BUILD_TAG) is not None: - GlobalVariables.CI_LOOP = "daily" + if CONST.BUILD_TAG is not None: + if re.search("daily", CONST.BUILD_TAG) is not None: + CONST.CI_LOOP = "daily" else: - GlobalVariables.CI_LOOP = "weekly" + CONST.CI_LOOP = "weekly" str = '' str += print_separator('=', delimiter="=") @@ -122,19 +120,19 @@ def main(args): str += print_line_no_columns(' ') str += print_line_no_columns(" Deployment description:") str += print_line_no_columns(" INSTALLER: %s" - % GlobalVariables.INSTALLER) - if GlobalVariables.SCENARIO is not None: + % CONST.INSTALLER_TYPE) + if CONST.DEPLOY_SCENARIO is not None: str += print_line_no_columns(" SCENARIO: %s" - % GlobalVariables.SCENARIO) - if GlobalVariables.BUILD_TAG is not None: + % CONST.DEPLOY_SCENARIO) + if CONST.BUILD_TAG is not None: str += print_line_no_columns(" BUILD TAG: %s" - % GlobalVariables.BUILD_TAG) - if GlobalVariables.CI_LOOP is not None: + % CONST.BUILD_TAG) + if CONST.CI_LOOP is not None: str += print_line_no_columns(" CI LOOP: %s" - % GlobalVariables.CI_LOOP) + % CONST.CI_LOOP) str += print_line_no_columns(' ') str += print_separator('=') - if GlobalVariables.IS_CI_RUN: + if CONST.IS_CI_RUN: str += print_line('TEST CASE', 'TIER', 'DURATION', 'RESULT', 'URL') else: str += print_line('TEST CASE', 'TIER', 'DURATION', 'RESULT') diff --git a/functest/ci/prepare_env.py b/functest/ci/prepare_env.py index 3a99d3bac..3df3a0e0e 100755 --- a/functest/ci/prepare_env.py +++ b/functest/ci/prepare_env.py @@ -13,19 +13,20 @@ # +import argparse import json import os import re import subprocess import sys -import argparse import yaml +from opnfv.utils import constants as opnfv_constants import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils import functest.utils.openstack_utils as os_utils -import functest.utils.functest_constants as ft_constants +from functest.utils.constants import CONST actions = ['start', 'check'] parser = argparse.ArgumentParser() @@ -39,7 +40,7 @@ args = parser.parse_args() logger = ft_logger.Logger("prepare_env").getLogger() -CONFIG_FUNCTEST_PATH = ft_constants.CONFIG_FUNCTEST_YAML +CONFIG_FUNCTEST_PATH = CONST.CONFIG_FUNCTEST_YAML CONFIG_PATCH_PATH = os.path.join(os.path.dirname( CONFIG_FUNCTEST_PATH), "config_patch.yaml") @@ -55,95 +56,97 @@ def check_env_variables(): print_separator() logger.info("Checking environment variables...") - if ft_constants.CI_INSTALLER_TYPE is None: + if CONST.INSTALLER_TYPE is None: logger.warning("The env variable 'INSTALLER_TYPE' is not defined.") - ft_constants.CI_INSTALLER_TYPE = "undefined" + CONST.INSTALLER_TYPE = "undefined" else: - if ft_constants.CI_INSTALLER_TYPE not in ft_constants.INSTALLERS: + if CONST.INSTALLER_TYPE not in opnfv_constants.INSTALLERS: logger.warning("INSTALLER_TYPE=%s is not a valid OPNFV installer. " "Available OPNFV Installers are : %s. " "Setting INSTALLER_TYPE=undefined." - % (ft_constants.CI_INSTALLER_TYPE, - ft_constants.INSTALLERS)) - ft_constants.CI_INSTALLER_TYPE = "undefined" + % (CONST.INSTALLER_TYPE, + opnfv_constants.INSTALLERS)) + CONST.INSTALLER_TYPE = "undefined" else: logger.info(" INSTALLER_TYPE=%s" - % ft_constants.CI_INSTALLER_TYPE) + % CONST.INSTALLER_TYPE) - if ft_constants.CI_INSTALLER_IP is None: + if CONST.INSTALLER_IP is None: logger.warning("The env variable 'INSTALLER_IP' is not defined. " "It is needed to fetch the OpenStack credentials. " "If the credentials are not provided to the " "container as a volume, please add this env variable " "to the 'docker run' command.") else: - logger.info(" INSTALLER_IP=%s" % ft_constants.CI_INSTALLER_IP) + logger.info(" INSTALLER_IP=%s" % CONST.INSTALLER_IP) - if ft_constants.CI_SCENARIO is None: + if CONST.DEPLOY_SCENARIO is None: logger.warning("The env variable 'DEPLOY_SCENARIO' is not defined. " "Setting CI_SCENARIO=undefined.") - ft_constants.CI_SCENARIO = "undefined" + CONST.DEPLOY_SCENARIO = "undefined" else: - logger.info(" DEPLOY_SCENARIO=%s" % ft_constants.CI_SCENARIO) - if ft_constants.CI_DEBUG: - logger.info(" CI_DEBUG=%s" % ft_constants.CI_DEBUG) + logger.info(" DEPLOY_SCENARIO=%s" % CONST.DEPLOY_SCENARIO) + if CONST.CI_DEBUG: + logger.info(" CI_DEBUG=%s" % CONST.CI_DEBUG) - if ft_constants.CI_NODE: - logger.info(" NODE_NAME=%s" % ft_constants.CI_NODE) + if CONST.NODE_NAME: + logger.info(" NODE_NAME=%s" % CONST.NODE_NAME) - if ft_constants.CI_BUILD_TAG: - logger.info(" BUILD_TAG=%s" % ft_constants.CI_BUILD_TAG) + if CONST.BUILD_TAG: + logger.info(" BUILD_TAG=%s" % CONST.BUILD_TAG) - if ft_constants.IS_CI_RUN: - logger.info(" IS_CI_RUN=%s" % ft_constants.IS_CI_RUN) + if CONST.IS_CI_RUN: + logger.info(" IS_CI_RUN=%s" % CONST.IS_CI_RUN) def create_directories(): print_separator() logger.info("Creating needed directories...") - if not os.path.exists(ft_constants.FUNCTEST_CONF_DIR): - os.makedirs(ft_constants.FUNCTEST_CONF_DIR) - logger.info(" %s created." % ft_constants.FUNCTEST_CONF_DIR) + if not os.path.exists(CONST.dir_functest_conf): + os.makedirs(CONST.dir_functest_conf) + logger.info(" %s created." % CONST.dir_functest_conf) else: logger.debug(" %s already exists." - % ft_constants.FUNCTEST_CONF_DIR) + % CONST.dir_functest_conf) - if not os.path.exists(ft_constants.FUNCTEST_DATA_DIR): - os.makedirs(ft_constants.FUNCTEST_DATA_DIR) - logger.info(" %s created." % ft_constants.FUNCTEST_DATA_DIR) + if not os.path.exists(CONST.dir_functest_data): + os.makedirs(CONST.dir_functest_data) + logger.info(" %s created." % CONST.dir_functest_data) else: logger.debug(" %s already exists." - % ft_constants.FUNCTEST_DATA_DIR) + % CONST.dir_functest_data) def source_rc_file(): print_separator() logger.info("Fetching RC file...") - if ft_constants.OPENSTACK_CREDS is None: + if CONST.openstack_creds is None: logger.warning("The environment variable 'creds' must be set and" "pointing to the local RC file. Using default: " "/home/opnfv/functest/conf/openstack.creds ...") - os.path.join(ft_constants.FUNCTEST_CONF_DIR, 'openstack.creds') + os.path.join(CONST.dir_functest_conf, 'openstack.creds') - if not os.path.isfile(ft_constants.OPENSTACK_CREDS): + if not os.path.isfile(CONST.openstack_creds): logger.info("RC file not provided. " "Fetching it from the installer...") - if ft_constants.CI_INSTALLER_IP is None: + if CONST.INSTALLER_IP is None: logger.error("The env variable CI_INSTALLER_IP must be provided in" " order to fetch the credentials from the installer.") sys.exit("Missing CI_INSTALLER_IP.") - if ft_constants.CI_INSTALLER_TYPE not in ft_constants.INSTALLERS: + if CONST.INSTALLER_TYPE not in opnfv_constants.INSTALLERS: logger.error("Cannot fetch credentials. INSTALLER_TYPE=%s is " "not a valid OPNFV installer. Available " - "installers are : %s." % ft_constants.INSTALLERS) + "installers are : %s." % + (CONST.INSTALLER_TYPE, + opnfv_constants.INSTALLERS)) sys.exit("Wrong INSTALLER_TYPE.") cmd = ("/home/opnfv/repos/releng/utils/fetch_os_creds.sh " "-d %s -i %s -a %s" - % (ft_constants.OPENSTACK_CREDS, - ft_constants.CI_INSTALLER_TYPE, - ft_constants.CI_INSTALLER_IP)) + % (CONST.openstack_creds, + CONST.INSTALLER_TYPE, + CONST.INSTALLER_IP)) logger.debug("Executing command: %s" % cmd) p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) output = p.communicate()[0] @@ -153,38 +156,38 @@ def source_rc_file(): sys.exit(1) else: logger.info("RC file provided in %s." - % ft_constants.OPENSTACK_CREDS) - if os.path.getsize(ft_constants.OPENSTACK_CREDS) == 0: + % CONST.openstack_creds) + if os.path.getsize(CONST.openstack_creds) == 0: logger.error("The file %s is empty." - % ft_constants.OPENSTACK_CREDS) + % CONST.openstack_creds) sys.exit(1) logger.info("Sourcing the OpenStack RC file...") creds = os_utils.source_credentials( - ft_constants.OPENSTACK_CREDS) + CONST.openstack_creds) str = "" for key, value in creds.iteritems(): if re.search("OS_", key): str += "\n\t\t\t\t\t\t " + key + "=" + value if key == 'OS_AUTH_URL': - ft_constants.OS_AUTH_URL = value + CONST.OS_AUTH_URL = value elif key == 'OS_USERNAME': - ft_constants.OS_USERNAME = value + CONST.OS_USERNAME = value elif key == 'OS_TENANT_NAME': - ft_constants.OS_TENANT_NAME = value + CONST.OS_TENANT_NAME = value elif key == 'OS_PASSWORD': - ft_constants.OS_PASSWORD = value + CONST.OS_PASSWORD = value logger.debug("Used credentials: %s" % str) - logger.debug("OS_AUTH_URL:%s" % ft_constants.OS_AUTH_URL) - logger.debug("OS_USERNAME:%s" % ft_constants.OS_USERNAME) - logger.debug("OS_TENANT_NAME:%s" % ft_constants.OS_TENANT_NAME) - logger.debug("OS_PASSWORD:%s" % ft_constants.OS_PASSWORD) + logger.debug("OS_AUTH_URL:%s" % CONST.OS_AUTH_URL) + logger.debug("OS_USERNAME:%s" % CONST.OS_USERNAME) + logger.debug("OS_TENANT_NAME:%s" % CONST.OS_TENANT_NAME) + logger.debug("OS_PASSWORD:%s" % CONST.OS_PASSWORD) def patch_config_file(): updated = False for key in functest_patch_yaml: - if key in ft_constants.CI_SCENARIO: + if key in CONST.DEPLOY_SCENARIO: new_functest_yaml = dict(ft_utils.merge_dicts( ft_utils.get_functest_yaml(), functest_patch_yaml[key])) updated = True @@ -199,7 +202,7 @@ def patch_config_file(): def verify_deployment(): print_separator() logger.info("Verifying OpenStack services...") - cmd = ("%s/functest/ci/check_os.sh" % ft_constants.FUNCTEST_REPO_DIR) + cmd = ("%s/functest/ci/check_os.sh" % CONST.dir_repo_functest) logger.debug("Executing command: %s" % cmd) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) @@ -219,19 +222,19 @@ def install_rally(): cmd = "rally deployment destroy opnfv-rally" ft_utils.execute_command(cmd, error_msg=( "Deployment %s does not exist." - % ft_constants.RALLY_DEPLOYMENT_NAME), + % CONST.rally_deployment_name), verbose=False) rally_conf = os_utils.get_credentials_for_rally() with open('rally_conf.json', 'w') as fp: json.dump(rally_conf, fp) cmd = "rally deployment create --file=rally_conf.json --name=" - cmd += ft_constants.RALLY_DEPLOYMENT_NAME + cmd += CONST.rally_deployment_name ft_utils.execute_command(cmd, error_msg="Problem creating Rally deployment") logger.info("Installing tempest from existing repo...") cmd = ("rally verify install --source " + - ft_constants.TEMPEST_REPO_DIR + + CONST.dir_repo_tempest + " --system-wide") ft_utils.execute_command(cmd, error_msg="Problem installing Tempest.") @@ -254,11 +257,11 @@ def install_rally(): def check_environment(): msg_not_active = "The Functest environment is not installed." - if not os.path.isfile(ft_constants.ENV_FILE): + if not os.path.isfile(CONST.env_active): logger.error(msg_not_active) sys.exit(1) - with open(ft_constants.ENV_FILE, "r") as env_file: + with open(CONST.env_active, "r") as env_file: s = env_file.read() if not re.search("1", s): logger.error(msg_not_active) @@ -281,7 +284,7 @@ def main(): verify_deployment() install_rally() - with open(ft_constants.ENV_FILE, "w") as env_file: + with open(CONST.env_active, "w") as env_file: env_file.write("1") check_environment() diff --git a/functest/ci/run_tests.py b/functest/ci/run_tests.py index 3f02c8725..7aac9d2cf 100644..100755 --- a/functest/ci/run_tests.py +++ b/functest/ci/run_tests.py @@ -8,24 +8,23 @@ # http://www.apache.org/licenses/LICENSE-2.0 # +import argparse import datetime import importlib import os import re import sys -import argparse - import functest.ci.generate_report as generate_report import functest.ci.tier_builder as tb import functest.core.testcase_base as testcase_base +import functest.utils.functest_constants as ft_constants import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils -import functest.utils.functest_constants as ft_constants import functest.utils.openstack_clean as os_clean import functest.utils.openstack_snapshot as os_snapshot import functest.utils.openstack_utils as os_utils - +from functest.utils.constants import CONST parser = argparse.ArgumentParser() parser.add_argument("-t", "--test", dest="test", action='store', @@ -44,7 +43,7 @@ logger = ft_logger.Logger("run_tests").getLogger() """ global variables """ -EXEC_SCRIPT = ("%s/functest/ci/exec_test.sh" % ft_constants.FUNCTEST_REPO_DIR) +EXEC_SCRIPT = ("%s/functest/ci/exec_test.sh" % CONST.dir_repo_functest) # This will be the return code of this script. If any of the tests fails, # this variable will change to -1 @@ -65,7 +64,7 @@ def print_separator(str, count=45): def source_rc_file(): - rc_file = ft_constants.OPENSTACK_CREDS + rc_file = CONST.openstack_creds if not os.path.isfile(rc_file): logger.error("RC file %s does not exist..." % rc_file) sys.exit(1) @@ -75,16 +74,20 @@ def source_rc_file(): if re.search("OS_", key): if key == 'OS_AUTH_URL': ft_constants.OS_AUTH_URL = value + CONST.OS_AUTH_URL = value elif key == 'OS_USERNAME': ft_constants.OS_USERNAME = value + CONST.OS_USERNAME = value elif key == 'OS_TENANT_NAME': ft_constants.OS_TENANT_NAME = value + CONST.OS_TENANT_NAME = value elif key == 'OS_PASSWORD': ft_constants.OS_PASSWORD = value - logger.debug("OS_AUTH_URL:%s" % ft_constants.OS_AUTH_URL) - logger.debug("OS_USERNAME:%s" % ft_constants.OS_USERNAME) - logger.debug("OS_TENANT_NAME:%s" % ft_constants.OS_TENANT_NAME) - logger.debug("OS_PASSWORD:%s" % ft_constants.OS_PASSWORD) + CONST.OS_PASSWORD = value + logger.debug("OS_AUTH_URL:%s" % CONST.OS_AUTH_URL) + logger.debug("OS_USERNAME:%s" % CONST.OS_USERNAME) + logger.debug("OS_TENANT_NAME:%s" % CONST.OS_TENANT_NAME) + logger.debug("OS_PASSWORD:%s" % CONST.OS_PASSWORD) def generate_os_snapshot(): @@ -124,6 +127,7 @@ def run_test(test, tier_name): logger.info("Running test case '%s'..." % test_name) print_separator("=") logger.debug("\n%s" % test) + source_rc_file() if GlobalVariables.CLEAN_FLAG: generate_os_snapshot() @@ -140,9 +144,10 @@ def run_test(test, tier_name): cls = getattr(module, run_dict['class']) test_case = cls() result = test_case.run() - if (result == testcase_base.TestcaseBase.EX_OK and - GlobalVariables.REPORT_FLAG): - test_case.push_to_db() + if result == testcase_base.TestcaseBase.EX_OK: + if GlobalVariables.REPORT_FLAG: + test_case.push_to_db() + result = test_case.check_criteria() except ImportError: logger.exception("Cannot import module {}".format( run_dict['module'])) @@ -199,17 +204,11 @@ def run_tier(tier): def run_all(tiers): summary = "" - BUILD_TAG = ft_constants.CI_BUILD_TAG - if BUILD_TAG is not None and re.search("daily", BUILD_TAG) is not None: - CI_LOOP = "daily" - else: - CI_LOOP = "weekly" - tiers_to_run = [] for tier in tiers.get_tiers(): if (len(tier.get_tests()) != 0 and - re.search(CI_LOOP, tier.get_ci_loop()) is not None): + re.search(CONST.CI_LOOP, tier.get_ci_loop()) is not None): tiers_to_run.append(tier) summary += ("\n - %s:\n\t %s" % (tier.get_name(), @@ -225,10 +224,10 @@ def run_all(tiers): def main(): - CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE - CI_SCENARIO = ft_constants.CI_SCENARIO + CI_INSTALLER_TYPE = CONST.INSTALLER_TYPE + CI_SCENARIO = CONST.DEPLOY_SCENARIO - file = ft_constants.FUNCTEST_TESTCASES_YAML + file = CONST.functest_testcases_yaml _tiers = tb.TierBuilder(CI_INSTALLER_TYPE, CI_SCENARIO, file) if args.noclean: diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index 6a11a03f4..6f57c7030 100755 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -67,7 +67,9 @@ tiers: dependencies: installer: '' scenario: '' - + run: + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestSmokeSerial' - name: rally_sanity criteria: 'success_rate == 100%' @@ -109,7 +111,7 @@ tiers: - name: connection_check criteria: 'status == "PASS"' - blocking: true + blocking: false description: >- This test case verifies the retrieval of OpenStack clients: Keystone, Glance, Neutron and Nova and may perform some @@ -127,7 +129,7 @@ tiers: - name: api_check criteria: 'status == "PASS"' - blocking: true + blocking: false description: >- This test case verifies the retrieval of OpenStack clients: Keystone, Glance, Neutron and Nova and may perform some @@ -145,7 +147,7 @@ tiers: - name: snaps_smoke criteria: 'status == "PASS"' - blocking: true + blocking: false description: >- This test case contains tests that setup and destroy environments with VMs with and without Floating IPs @@ -222,6 +224,10 @@ tiers: dependencies: installer: '(apex)|(joid)' scenario: '^((?!fdio|lxd).)*$' + run: + module: 'functest.opnfv_tests.features.copper' + class: 'Copper' + - name: moon criteria: 'status == "PASS"' @@ -240,6 +246,9 @@ tiers: dependencies: installer: '(fuel)|(compass)' scenario: 'multisite' + run: + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestMultisite' - name: odl-sfc criteria: 'status == "PASS"' @@ -249,6 +258,9 @@ tiers: dependencies: installer: '(apex)|(fuel)' scenario: 'odl_l2-sfc' + run: + module: 'functest.opnfv_tests.features.odl_sfc' + class: 'OpenDaylightSFC' - name: onos_sfc criteria: 'status == "PASS"' @@ -271,7 +283,7 @@ tiers: module: 'functest.opnfv_tests.vnf.rnc.parser' class: 'Parser' - - name: openstack + name: components order: 3 ci_loop: 'weekly' description : >- @@ -288,6 +300,9 @@ tiers: dependencies: installer: '' scenario: '' + run: + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestFullParallel' - name: rally_full diff --git a/functest/cli/cli_base.py b/functest/cli/cli_base.py index 3b14fa336..cc697ed7e 100644 --- a/functest/cli/cli_base.py +++ b/functest/cli/cli_base.py @@ -121,8 +121,11 @@ def testcase_show(testname): @click.option('-n', '--noclean', is_flag=True, default=False, help='The created openstack resources by the test' 'will not be cleaned after the execution.') -def testcase_run(testname, noclean): - _testcase.run(testname, noclean) +@click.option('-r', '--report', is_flag=True, default=False, + help='Push results to the results DataBase. Only CI Pods' + 'have rights to do that.') +def testcase_run(testname, noclean, report): + _testcase.run(testname, noclean, report) @tier.command('list', help="Lists the available tiers.") @@ -147,5 +150,8 @@ def tier_gettests(tiername): @click.option('-n', '--noclean', is_flag=True, default=False, help='The created openstack resources by the tests' 'will not be cleaned after the execution.') -def tier_run(tiername, noclean): - _tier.run(tiername, noclean) +@click.option('-r', '--report', is_flag=True, default=False, + help='Push results to the results DataBase. Only CI Pods' + 'have rights to do that.') +def tier_run(tiername, noclean, report): + _tier.run(tiername, noclean, report) diff --git a/functest/cli/commands/cli_env.py b/functest/cli/commands/cli_env.py index 9f793e71f..9423631bf 100644 --- a/functest/cli/commands/cli_env.py +++ b/functest/cli/commands/cli_env.py @@ -12,8 +12,8 @@ import os import click import git +from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils -import functest.utils.functest_constants as ft_constants class CliEnv: @@ -28,7 +28,7 @@ class CliEnv: "it again? [y|n]\n") while True: if answer.lower() in ["y", "yes"]: - os.remove(ft_constants.ENV_FILE) + os.remove(CONST.env_active) break elif answer.lower() in ["n", "no"]: return @@ -36,40 +36,32 @@ class CliEnv: answer = raw_input("Invalid answer. Please type [y|n]\n") cmd = ("python %s/functest/ci/prepare_env.py start" % - ft_constants.FUNCTEST_REPO_DIR) + CONST.dir_repo_functest) ft_utils.execute_command(cmd) def show(self): - CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE - if CI_INSTALLER_TYPE is None: - CI_INSTALLER_TYPE = "Unknown" - CI_INSTALLER_IP = ft_constants.CI_INSTALLER_IP - if CI_INSTALLER_IP is None: - CI_INSTALLER_IP = "Unknown" - CI_INSTALLER = ("%s, %s" % (CI_INSTALLER_TYPE, CI_INSTALLER_IP)) - - CI_SCENARIO = ft_constants.CI_SCENARIO - if CI_SCENARIO is None: - CI_SCENARIO = "Unknown" - - CI_NODE = ft_constants.CI_NODE - if CI_NODE is None: - CI_NODE = "Unknown" - - repo = git.Repo(ft_constants.FUNCTEST_REPO_DIR) - branch = repo.head.reference - GIT_BRANCH = branch.name - GIT_HASH = branch.commit.hexsha - - CI_BUILD_TAG = ft_constants.CI_BUILD_TAG - if CI_BUILD_TAG is not None: - CI_BUILD_TAG = CI_BUILD_TAG.lstrip( + def _get_value(attr, default='Unknown'): + return attr if attr else default + + install_type = _get_value(CONST.INSTALLER_TYPE) + installer_ip = _get_value(CONST.INSTALLER_IP) + installer_info = ("%s, %s" % (install_type, installer_ip)) + scenario = _get_value(CONST.DEPLOY_SCENARIO) + node = _get_value(CONST.NODE_NAME) + repo_h = git.Repo(CONST.dir_repo_functest).head + if repo_h.is_detached: + git_branch = 'detached from FETCH_HEAD' + git_hash = repo_h.commit.hexsha + else: + branch = repo_h.reference + git_branch = branch.name + git_hash = branch.commit.hexsha + is_debug = _get_value(CONST.CI_DEBUG, 'false') + build_tag = CONST.BUILD_TAG + if build_tag is not None: + build_tag = build_tag.lstrip( "jenkins-").lstrip("functest").lstrip("-") - CI_DEBUG = ft_constants.CI_DEBUG - if CI_DEBUG is None: - CI_DEBUG = "false" - STATUS = "not ready" if self.status(verbose=False) == 0: STATUS = "ready" @@ -77,14 +69,14 @@ class CliEnv: click.echo("+======================================================+") click.echo("| Functest Environment info |") click.echo("+======================================================+") - click.echo("| INSTALLER: %s|" % CI_INSTALLER.ljust(41)) - click.echo("| SCENARIO: %s|" % CI_SCENARIO.ljust(41)) - click.echo("| POD: %s|" % CI_NODE.ljust(41)) - click.echo("| GIT BRACNH: %s|" % GIT_BRANCH.ljust(41)) - click.echo("| GIT HASH: %s|" % GIT_HASH.ljust(41)) - if CI_BUILD_TAG: - click.echo("| BUILD TAG: %s|" % CI_BUILD_TAG.ljust(41)) - click.echo("| DEBUG FLAG: %s|" % CI_DEBUG.ljust(41)) + click.echo("| INSTALLER: %s|" % installer_info.ljust(41)) + click.echo("| SCENARIO: %s|" % scenario.ljust(41)) + click.echo("| POD: %s|" % node.ljust(41)) + click.echo("| GIT BRACNH: %s|" % git_branch.ljust(41)) + click.echo("| GIT HASH: %s|" % git_hash.ljust(41)) + if build_tag: + click.echo("| BUILD TAG: %s|" % build_tag.ljust(41)) + click.echo("| DEBUG FLAG: %s|" % is_debug.ljust(41)) click.echo("+------------------------------------------------------+") click.echo("| STATUS: %s|" % STATUS.ljust(41)) click.echo("+------------------------------------------------------+") @@ -92,7 +84,7 @@ class CliEnv: def status(self, verbose=True): ret_val = 0 - if not os.path.isfile(ft_constants.ENV_FILE): + if not os.path.isfile(CONST.env_active): if verbose: click.echo("Functest environment is not installed.\n") ret_val = 1 diff --git a/functest/cli/commands/cli_os.py b/functest/cli/commands/cli_os.py index bb8592195..aeb34974f 100644 --- a/functest/cli/commands/cli_os.py +++ b/functest/cli/commands/cli_os.py @@ -12,23 +12,21 @@ import os import click +from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils import functest.utils.openstack_clean as os_clean import functest.utils.openstack_snapshot as os_snapshot -import functest.utils.functest_constants as ft_constants - - -OPENSTACK_RC_FILE = ft_constants.OPENSTACK_CREDS -OPENSTACK_SNAPSHOT_FILE = ft_constants.OPENSTACK_SNAPSHOT_FILE class CliOpenStack: def __init__(self): - self.os_auth_url = ft_constants.OS_AUTH_URL + self.os_auth_url = CONST.OS_AUTH_URL self.endpoint_ip = None self.endpoint_port = None - if self.os_auth_url is not None: + self.openstack_creds = CONST.openstack_creds + self.snapshot_file = CONST.openstack_snapshot_file + if self.os_auth_url: self.endpoint_ip = self.os_auth_url.rsplit("/")[2].rsplit(":")[0] self.endpoint_port = self.os_auth_url.rsplit("/")[2].rsplit(":")[1] @@ -43,13 +41,14 @@ class CliOpenStack: click.echo("Cannot talk to the endpoint %s\n" % self.endpoint_ip) exit(0) - def show_credentials(self): + @staticmethod + def show_credentials(): for key, value in os.environ.items(): if key.startswith('OS_'): click.echo("{}={}".format(key, value)) def fetch_credentials(self): - if os.path.isfile(OPENSTACK_RC_FILE): + if os.path.isfile(self.openstack_creds): answer = raw_input("It seems the RC file is already present. " "Do you want to overwrite it? [y|n]\n") while True: @@ -60,31 +59,31 @@ class CliOpenStack: else: answer = raw_input("Invalid answer. Please type [y|n]\n") - CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE - if CI_INSTALLER_TYPE is None: + installer_type = CONST.INSTALLER_TYPE + if installer_type is None: click.echo("The environment variable 'INSTALLER_TYPE' is not" "defined. Please export it") - CI_INSTALLER_IP = ft_constants.CI_INSTALLER_IP - if CI_INSTALLER_IP is None: + installer_ip = CONST.INSTALLER_IP + if installer_ip is None: click.echo("The environment variable 'INSTALLER_IP' is not" "defined. Please export it") cmd = ("%s/releng/utils/fetch_os_creds.sh -d %s -i %s -a %s" - % (ft_constants.REPOS_DIR, - OPENSTACK_RC_FILE, - CI_INSTALLER_TYPE, - CI_INSTALLER_IP)) + % (CONST.dir_repos, + self.openstack_creds, + installer_type, + installer_ip)) click.echo("Fetching credentials from installer node '%s' with IP=%s.." - % (CI_INSTALLER_TYPE, CI_INSTALLER_IP)) + % (installer_type, installer_ip)) ft_utils.execute_command(cmd, verbose=False) def check(self): self.ping_endpoint() - cmd = ft_constants.FUNCTEST_REPO_DIR + "/functest/ci/check_os.sh" + cmd = CONST.dir_repo_functest + "/functest/ci/check_os.sh" ft_utils.execute_command(cmd, verbose=False) def snapshot_create(self): self.ping_endpoint() - if os.path.isfile(OPENSTACK_SNAPSHOT_FILE): + if os.path.isfile(self.snapshot_file): answer = raw_input("It seems there is already an OpenStack " "snapshot. Do you want to overwrite it with " "the current OpenStack status? [y|n]\n") @@ -100,18 +99,18 @@ class CliOpenStack: os_snapshot.main() def snapshot_show(self): - if not os.path.isfile(OPENSTACK_SNAPSHOT_FILE): + if not os.path.isfile(self.snapshot_file): click.echo("There is no OpenStack snapshot created. To create " "one run the command " "'functest openstack snapshot-create'") return - with open(OPENSTACK_SNAPSHOT_FILE, 'r') as yaml_file: + with open(self.snapshot_file, 'r') as yaml_file: click.echo("\n%s" % yaml_file.read()) def clean(self): self.ping_endpoint() - if not os.path.isfile(OPENSTACK_SNAPSHOT_FILE): + if not os.path.isfile(self.snapshot_file): click.echo("Not possible to clean OpenStack without a snapshot. " "This could cause problems. " "Run first the command " diff --git a/functest/cli/commands/cli_testcase.py b/functest/cli/commands/cli_testcase.py index efe177d52..b6566245a 100644 --- a/functest/cli/commands/cli_testcase.py +++ b/functest/cli/commands/cli_testcase.py @@ -14,19 +14,17 @@ import os import click import functest.ci.tier_builder as tb +from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils import functest.utils.functest_vacation as vacation -import functest.utils.functest_constants as ft_constants class CliTestcase: def __init__(self): - CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE - CI_SCENARIO = ft_constants.CI_SCENARIO - testcases = ft_constants.FUNCTEST_TESTCASES_YAML - - self.tiers = tb.TierBuilder(CI_INSTALLER_TYPE, CI_SCENARIO, testcases) + self.tiers = tb.TierBuilder(CONST.INSTALLER_TYPE, + CONST.DEPLOY_SCENARIO, + CONST.functest_testcases_yaml) def list(self): summary = "" @@ -43,17 +41,23 @@ class CliTestcase: click.echo(description) - def run(self, testname, noclean=False): + @staticmethod + def run(testname, noclean=False, report=False): + + flags = "" + if noclean: + flags += "-n " + if report: + flags += "-r " + if testname == 'vacation': vacation.main() - elif not os.path.isfile(ft_constants.ENV_FILE): + elif not os.path.isfile(CONST.env_active): click.echo("Functest environment is not ready. " "Run first 'functest env prepare'") else: - if noclean: - cmd = ("python %s/functest/ci/run_tests.py " - "-n -t %s" % (ft_constants.FUNCTEST_REPO_DIR, testname)) - else: + tests = testname.split(",") + for test in tests: cmd = ("python %s/functest/ci/run_tests.py " - "-t %s" % (ft_constants.FUNCTEST_REPO_DIR, testname)) - ft_utils.execute_command(cmd) + "%s -t %s" % (CONST.dir_repo_functest, flags, test)) + ft_utils.execute_command(cmd) diff --git a/functest/cli/commands/cli_tier.py b/functest/cli/commands/cli_tier.py index 9da51072a..b9d25b6d0 100644 --- a/functest/cli/commands/cli_tier.py +++ b/functest/cli/commands/cli_tier.py @@ -14,17 +14,16 @@ import os import click import functest.ci.tier_builder as tb +from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils -import functest.utils.functest_constants as ft_constants class CliTier: def __init__(self): - CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE - CI_SCENARIO = ft_constants.CI_SCENARIO - testcases = ft_constants.FUNCTEST_TESTCASES_YAML - self.tiers = tb.TierBuilder(CI_INSTALLER_TYPE, CI_SCENARIO, testcases) + self.tiers = tb.TierBuilder(CONST.INSTALLER_TYPE, + CONST.DEPLOY_SCENARIO, + CONST.functest_testcases_yaml) def list(self): summary = "" @@ -54,15 +53,19 @@ class CliTier: tests = tier.get_test_names() click.echo("Test cases in tier '%s':\n %s\n" % (tiername, tests)) - def run(self, tiername, noclean=False): - if not os.path.isfile(ft_constants.ENV_FILE): + @staticmethod + def run(tiername, noclean=False, report=False): + + flags = "" + if noclean: + flags += "-n " + if report: + flags += "-r " + + if not os.path.isfile(CONST.env_active): click.echo("Functest environment is not ready. " "Run first 'functest env prepare'") else: - if noclean: - cmd = ("python %s/functest/ci/run_tests.py " - "-n -t %s" % (ft_constants.FUNCTEST_REPO_DIR, tiername)) - else: - cmd = ("python %s/functest/ci/run_tests.py " - "-t %s" % (ft_constants.FUNCTEST_REPO_DIR, tiername)) + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, flags, tiername)) ft_utils.execute_command(cmd) diff --git a/functest/core/feature_base.py b/functest/core/feature_base.py index 01a27f305..873e21dae 100644 --- a/functest/core/feature_base.py +++ b/functest/core/feature_base.py @@ -3,6 +3,7 @@ import time import testcase_base as base import functest.utils.functest_utils as ft_utils import functest.utils.functest_logger as ft_logger +from functest.utils.constants import CONST class FeatureBase(base.TestcaseBase): @@ -11,7 +12,7 @@ class FeatureBase(base.TestcaseBase): self.project_name = project self.case_name = case self.cmd = cmd - self.repo = self.get_conf('general.directories.{}'.format(repo)) + self.repo = CONST.__getattribute__(repo) self.result_file = self.get_result_file() self.logger = ft_logger.Logger(project).getLogger() @@ -44,15 +45,10 @@ class FeatureBase(base.TestcaseBase): return exit_code def get_result_file(self): - dir = self.get_conf('general.directories.dir_results') - return "{}/{}.log".format(dir, self.project_name) + return "{}/{}.log".format(CONST.dir_results, self.project_name) def log_results(self): ft_utils.logger_test_results(self.project_name, self.case_name, self.criteria, self.details) - - @staticmethod - def get_conf(parameter): - return ft_utils.get_functest_config(parameter) diff --git a/functest/core/pytest_suite_runner.py b/functest/core/pytest_suite_runner.py index 2d5b2667b..1eed92b57 100644..100755 --- a/functest/core/pytest_suite_runner.py +++ b/functest/core/pytest_suite_runner.py @@ -41,8 +41,8 @@ class PyTestSuiteRunner(base.TestcaseBase): for test, message in result.failures: self.logger.error(str(test) + " FAILED with " + message) - if (result.errors and len(result.errors) > 0) \ - or (result.failures and len(result.failures) > 0): + if ((result.errors and len(result.errors) > 0) + or (result.failures and len(result.failures) > 0)): self.logger.info("%s FAILED" % self.case_name) self.criteria = 'FAIL' exit_code = base.TestcaseBase.EX_RUN_ERROR diff --git a/functest/core/testcase_base.py b/functest/core/testcase_base.py index e869803d8..838b63983 100644 --- a/functest/core/testcase_base.py +++ b/functest/core/testcase_base.py @@ -18,6 +18,7 @@ class TestcaseBase(object): EX_OK = os.EX_OK EX_RUN_ERROR = os.EX_SOFTWARE EX_PUSH_TO_DB_ERROR = os.EX_SOFTWARE - 1 + EX_TESTCASE_FAILED = os.EX_SOFTWARE - 2 logger = ft_logger.Logger(__name__).getLogger() @@ -29,6 +30,15 @@ class TestcaseBase(object): self.start_time = "" self.stop_time = "" + def check_criteria(self): + try: + assert self.criteria + if self.criteria == 'PASS': + return TestcaseBase.EX_OK + except: + self.logger.error("Please run test before checking the results") + return TestcaseBase.EX_TESTCASE_FAILED + def run(self, **kwargs): self.logger.error("Run must be implemented") return TestcaseBase.EX_RUN_ERROR diff --git a/functest/opnfv_tests/features/copper.py b/functest/opnfv_tests/features/copper.py index d003779e8..8d5393c95 100755 --- a/functest/opnfv_tests/features/copper.py +++ b/functest/opnfv_tests/features/copper.py @@ -14,70 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import argparse -import sys -import time +import functest.core.feature_base as base -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as functest_utils -import functest.utils.functest_constants as ft_constants -parser = argparse.ArgumentParser() -parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") -args = parser.parse_args() - -COPPER_REPO_DIR = ft_constants.COPPER_REPO_DIR -RESULTS_DIR = ft_constants.FUNCTEST_RESULTS_DIR - -logger = ft_logger.Logger("copper").getLogger() - - -def main(): - cmd = "%s/tests/run.sh %s/tests" % (COPPER_REPO_DIR, COPPER_REPO_DIR) - - start_time = time.time() - - log_file = RESULTS_DIR + "/copper.log" - ret_val = functest_utils.execute_command(cmd, - output_file=log_file) - - stop_time = time.time() - duration = round(stop_time - start_time, 1) - if ret_val == 0: - logger.info("COPPER PASSED") - test_status = 'PASS' - else: - logger.info("COPPER FAILED") - test_status = 'FAIL' - - details = { - 'timestart': start_time, - 'duration': duration, - 'status': test_status, - } - functest_utils.logger_test_results("Copper", - "copper-notification", - details['status'], details) - try: - if args.report: - functest_utils.push_results_to_db("copper", - "copper-notification", - start_time, - stop_time, - details['status'], - details) - logger.info("COPPER results pushed to DB") - except: - logger.error("Error pushing results into Database '%s'" - % sys.exc_info()[0]) - - if ret_val != 0: - sys.exit(-1) - - sys.exit(0) - - -if __name__ == '__main__': - main() +class Copper(base.FeatureBase): + def __init__(self): + super(Copper, self).__init__(project='copper', + case='copper-notification', + repo='dir_repo_copper') + self.cmd = "%s/tests/run.sh %s/tests" % (self.repo, self.repo) diff --git a/functest/opnfv_tests/features/domino.py b/functest/opnfv_tests/features/domino.py index 341648f4b..b36220fa0 100755 --- a/functest/opnfv_tests/features/domino.py +++ b/functest/opnfv_tests/features/domino.py @@ -14,65 +14,12 @@ # 0.3: add report flag to push results when needed # 0.4: refactoring to match Test abstraction class -import argparse -import os -import sys -import time +import functest.core.feature_base as base -import functest.core.testcase_base as testcase_base -import functest.utils.functest_constants as ft_constants -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils - - -class DominoCases(testcase_base.TestcaseBase): - DOMINO_REPO = ft_constants.DOMINO_REPO_DIR - RESULTS_DIR = ft_constants.FUNCTEST_RESULTS_DIR - logger = ft_logger.Logger("domino").getLogger() +class Domino(base.FeatureBase): def __init__(self): - super(DominoCases, self).__init__() - self.project_name = "domino" - self.case_name = "domino-multinode" - - def main(self, **kwargs): - cmd = 'cd %s && ./tests/run_multinode.sh' % self.DOMINO_REPO - log_file = os.path.join(self.RESULTS_DIR, "domino.log") - start_time = time.time() - - ret = ft_utils.execute_command(cmd, - output_file=log_file) - - stop_time = time.time() - if ret == 0: - self.logger.info("domino OK") - status = 'PASS' - else: - self.logger.info("domino FAILED") - status = "FAIL" - - # report status only if tests run (FAIL OR PASS) - self.criteria = status - self.start_time = start_time - self.stop_time = stop_time - self.details = {} - - def run(self): - kwargs = {} - return self.main(**kwargs) - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") - args = vars(parser.parse_args()) - domino = DominoCases() - try: - result = domino.main(**args) - if result != testcase_base.TestcaseBase.EX_OK: - sys.exit(result) - if args['report']: - sys.exit(domino.push_to_db()) - except Exception: - sys.exit(testcase_base.TestcaseBase.EX_RUN_ERROR) + super(Domino, self).__init__(project='domino', + case='domino-multinode', + repo='dir_repo_domino') + self.cmd = 'cd %s && ./tests/run_multinode.sh' % self.repo diff --git a/functest/opnfv_tests/features/odl_sfc.py b/functest/opnfv_tests/features/odl_sfc.py new file mode 100644 index 000000000..3b68d4204 --- /dev/null +++ b/functest/opnfv_tests/features/odl_sfc.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# +# Copyright (c) 2016 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +import functest.core.feature_base as base + + +class OpenDaylightSFC(base.FeatureBase): + + def __init__(self): + super(OpenDaylightSFC, self).__init__(project='sfc', + case='functest-odl-sfc"', + repo='dir_repo_sfc') + dir_sfc_functest = '{}/sfc/tests/functest'.format(self.repo) + self.cmd = 'cd %s && python ./run_tests.py' % dir_sfc_functest diff --git a/functest/opnfv_tests/features/sdnvpn.py b/functest/opnfv_tests/features/sdnvpn.py index 451299eb3..1919a03c2 100644..100755 --- a/functest/opnfv_tests/features/sdnvpn.py +++ b/functest/opnfv_tests/features/sdnvpn.py @@ -7,70 +7,14 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # +import functest.core.feature_base as base -import argparse -import os -import sys -import time - -import functest.core.testcase_base as testcase_base -import functest.utils.functest_constants as ft_constants -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils - - -class SdnVpnTests(testcase_base.TestcaseBase): - SDNVPN_REPO_TESTS = os.path.join( - ft_constants.SDNVPN_REPO_DIR, "tests/functest") - logger = ft_logger.Logger("sdnvpn").getLogger() +class SdnVpnTests(base.FeatureBase): def __init__(self): - super(SdnVpnTests, self).__init__() - self.project_name = "sdnvpn" - self.case_name = "bgpvpn" - - def main(self, **kwargs): - os.chdir(self.SDNVPN_REPO_TESTS) - cmd = 'run_tests.py' - log_file = os.path.join( - ft_constants.FUNCTEST_RESULTS_DIR, "sdnvpn.log") - start_time = time.time() - - ret = ft_utils.execute_command(cmd, - output_file=log_file) - - stop_time = time.time() - if ret == 0: - self.logger.info("%s OK" % self.case_name) - status = 'PASS' - else: - self.logger.info("%s FAILED" % self.case_name) - status = "FAIL" - - # report status only if tests run (FAIL OR PASS) - self.criteria = status - self.start_time = start_time - self.stop_time = stop_time - self.details = {} - - def run(self): - kwargs = {} - return self.main(**kwargs) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") - args = vars(parser.parse_args()) - sdnvpn = SdnVpnTests() - try: - result = sdnvpn.main(**args) - if result != testcase_base.TestcaseBase.EX_OK: - sys.exit(result) - if args['report']: - sys.exit(sdnvpn.push_to_db()) - except Exception: - sys.exit(testcase_base.TestcaseBase.EX_RUN_ERROR) + super(SdnVpnTests, self).__init__(project='sdnvpn', + case='bgpvpn', + repo='dir_repo_sdnvpn') + dir_sfc_functest = '{}/sdnvpn/test/functest'.format(self.repo) + self.cmd = 'cd %s && python ./run_tests.py' % dir_sfc_functest diff --git a/functest/opnfv_tests/openstack/examples/create_instance_and_ip.py b/functest/opnfv_tests/openstack/examples/create_instance_and_ip.py index b4e2e519b..b44008642 100755 --- a/functest/opnfv_tests/openstack/examples/create_instance_and_ip.py +++ b/functest/opnfv_tests/openstack/examples/create_instance_and_ip.py @@ -14,9 +14,9 @@ import argparse import os import sys +from functest.utils.constants import CONST import functest.utils.functest_logger as ft_logger import functest.utils.openstack_utils as os_utils -import functest.utils.functest_constants as ft_constants parser = argparse.ArgumentParser() @@ -29,26 +29,26 @@ args = parser.parse_args() """ logging configuration """ logger = ft_logger.Logger("create_instance_and_ip").getLogger() -HOME = ft_constants.HOME + "/" +HOME = CONST.dir_home + "/" VM_BOOT_TIMEOUT = 180 -EXAMPLE_INSTANCE_NAME = ft_constants.EXAMPLE_INSTANCE_NAME -EXAMPLE_FLAVOR = ft_constants.EXAMPLE_FLAVOR -EXAMPLE_IMAGE_NAME = ft_constants.EXAMPLE_IMAGE_NAME -IMAGE_FILENAME = ft_constants.GLANCE_IMAGE_FILENAME -IMAGE_FORMAT = ft_constants.GLANCE_IMAGE_FORMAT -IMAGE_PATH = os.path.join(ft_constants.FUNCTEST_DATA_DIR, IMAGE_FILENAME) +EXAMPLE_INSTANCE_NAME = CONST.example_vm_name +EXAMPLE_FLAVOR = CONST.example_flavor +EXAMPLE_IMAGE_NAME = CONST.example_image_name +IMAGE_FILENAME = CONST.openstack_image_file_name +IMAGE_FORMAT = CONST.openstack_image_disk_format +IMAGE_PATH = os.path.join(CONST.dir_functest_data, IMAGE_FILENAME) # NEUTRON Private Network parameters -EXAMPLE_PRIVATE_NET_NAME = ft_constants.EXAMPLE_PRIVATE_NET_NAME -EXAMPLE_PRIVATE_SUBNET_NAME = ft_constants.EXAMPLE_PRIVATE_SUBNET_NAME -EXAMPLE_PRIVATE_SUBNET_CIDR = ft_constants.EXAMPLE_PRIVATE_SUBNET_CIDR -EXAMPLE_ROUTER_NAME = ft_constants.EXAMPLE_ROUTER_NAME +EXAMPLE_PRIVATE_NET_NAME = CONST.example_private_net_name +EXAMPLE_PRIVATE_SUBNET_NAME = CONST.example_private_subnet_name +EXAMPLE_PRIVATE_SUBNET_CIDR = CONST.example_private_subnet_cidr +EXAMPLE_ROUTER_NAME = CONST.example_router_name -EXAMPLE_SECGROUP_NAME = ft_constants.EXAMPLE_SECGROUP_NAME -EXAMPLE_SECGROUP_DESCR = ft_constants.EXAMPLE_SECGROUP_DESCR +EXAMPLE_SECGROUP_NAME = CONST.example_sg_name +EXAMPLE_SECGROUP_DESCR = CONST.example_sg_desc def main(): @@ -64,11 +64,12 @@ def main(): container="bare", public=True) - network_dic = os_utils.create_network_full(neutron_client, - EXAMPLE_PRIVATE_NET_NAME, - EXAMPLE_PRIVATE_SUBNET_NAME, - EXAMPLE_ROUTER_NAME, - EXAMPLE_PRIVATE_SUBNET_CIDR) + network_dic = os_utils.create_network_full( + neutron_client, + EXAMPLE_PRIVATE_NET_NAME, + EXAMPLE_PRIVATE_SUBNET_NAME, + EXAMPLE_ROUTER_NAME, + EXAMPLE_PRIVATE_SUBNET_CIDR) if not network_dic: logger.error( "There has been a problem when creating the neutron network") @@ -86,11 +87,11 @@ def main(): "Configuration:\n name=%s \n flavor=%s \n image=%s \n " "network=%s \n" % (EXAMPLE_INSTANCE_NAME, EXAMPLE_FLAVOR, image_id, network_id)) - instance = \ - os_utils.create_instance_and_wait_for_active(EXAMPLE_FLAVOR, - image_id, - network_id, - EXAMPLE_INSTANCE_NAME) + instance = os_utils.create_instance_and_wait_for_active( + EXAMPLE_FLAVOR, + image_id, + network_id, + EXAMPLE_INSTANCE_NAME) if instance is None: logger.error("Error while booting instance.") diff --git a/functest/opnfv_tests/openstack/healthcheck/healthcheck.sh b/functest/opnfv_tests/openstack/healthcheck/healthcheck.sh index e27cf4b4b..57aa0c707 100755 --- a/functest/opnfv_tests/openstack/healthcheck/healthcheck.sh +++ b/functest/opnfv_tests/openstack/healthcheck/healthcheck.sh @@ -228,10 +228,11 @@ sleep ${wait_time} # Check if flavor exists -if [[ -z $(nova flavor-list|grep $flavor) ]]; then +if [[ -z $(openstack flavor list -f value -c Name | fgrep -x $flavor) ]]; then # if given flavor doesn't exist, we create one debug "Flavor $flavor doesn't exist. Creating a new flavor." - nova flavor-create --is-public false ${flavor} auto 512 1 1 --is-public True + openstack flavor create ${flavor} --id auto --ram 512 --disk 1 --vcpus 1 + openstack flavor set ${flavor} --property hw:mem_page_size=any fi debug "Using flavor $flavor to boot the instances." diff --git a/functest/opnfv_tests/openstack/rally/run_rally-cert.py b/functest/opnfv_tests/openstack/rally/run_rally-cert.py index 6d8f01602..ec22b52d8 100755 --- a/functest/opnfv_tests/openstack/rally/run_rally-cert.py +++ b/functest/opnfv_tests/openstack/rally/run_rally-cert.py @@ -15,20 +15,20 @@ # """ tests configuration """ +import argparse import json import os import re import subprocess import time -import argparse import iniparse import yaml +from functest.utils.constants import CONST import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils import functest.utils.openstack_utils as os_utils -import functest.utils.functest_constants as ft_constants tests = ['authenticate', 'glance', 'cinder', 'heat', 'keystone', 'neutron', 'nova', 'quotas', 'requests', 'vm', 'all'] @@ -71,8 +71,7 @@ else: """ logging configuration """ logger = ft_logger.Logger("run_rally-cert").getLogger() -RALLY_DIR = os.path.join(ft_constants.FUNCTEST_REPO_DIR, - ft_constants.RALLY_RELATIVE_PATH) +RALLY_DIR = os.path.join(CONST.dir_repo_functest, CONST.dir_rally) RALLY_SCENARIO_DIR = os.path.join(RALLY_DIR, "scenario") SANITY_MODE_DIR = os.path.join(RALLY_SCENARIO_DIR, "sanity") FULL_MODE_DIR = os.path.join(RALLY_SCENARIO_DIR, "full") @@ -87,19 +86,19 @@ TENANTS_AMOUNT = 3 ITERATIONS_AMOUNT = 10 CONCURRENCY = 4 -RESULTS_DIR = os.path.join(ft_constants.FUNCTEST_RESULTS_DIR, 'rally') -TEMPEST_CONF_FILE = os.path.join(ft_constants.FUNCTEST_RESULTS_DIR, +RESULTS_DIR = os.path.join(CONST.dir_results, 'rally') +TEMPEST_CONF_FILE = os.path.join(CONST.dir_results, 'tempest/tempest.conf') -RALLY_PRIVATE_NET_NAME = ft_constants.RALLY_PRIVATE_NET_NAME -RALLY_PRIVATE_SUBNET_NAME = ft_constants.RALLY_PRIVATE_SUBNET_NAME -RALLY_PRIVATE_SUBNET_CIDR = ft_constants.RALLY_PRIVATE_SUBNET_CIDR -RALLY_ROUTER_NAME = ft_constants.RALLY_ROUTER_NAME +RALLY_PRIVATE_NET_NAME = CONST.rally_network_name +RALLY_PRIVATE_SUBNET_NAME = CONST.rally_subnet_name +RALLY_PRIVATE_SUBNET_CIDR = CONST.rally_subnet_cidr +RALLY_ROUTER_NAME = CONST.rally_router_name -GLANCE_IMAGE_NAME = ft_constants.GLANCE_IMAGE_NAME -GLANCE_IMAGE_FILENAME = ft_constants.GLANCE_IMAGE_FILENAME -GLANCE_IMAGE_FORMAT = ft_constants.GLANCE_IMAGE_FORMAT -GLANCE_IMAGE_PATH = os.path.join(ft_constants.FUNCTEST_DATA_DIR, +GLANCE_IMAGE_NAME = CONST.openstack_image_name +GLANCE_IMAGE_FILENAME = CONST.openstack_image_file_name +GLANCE_IMAGE_FORMAT = CONST.openstack_image_disk_format +GLANCE_IMAGE_PATH = os.path.join(CONST.dir_functest_data, GLANCE_IMAGE_FILENAME) CINDER_VOLUME_TYPE_NAME = "volume_test" @@ -181,7 +180,7 @@ def build_task_args(test_file_name): net_id = GlobalVariables.network_dict['net_id'] task_args['netid'] = str(net_id) - auth_url = ft_constants.OS_AUTH_URL + auth_url = CONST.OS_AUTH_URL if auth_url is not None: task_args['request_url'] = auth_url.rsplit(":", 1)[0] else: @@ -271,8 +270,8 @@ def excl_scenario(): with open(BLACKLIST_FILE, 'r') as black_list_file: black_list_yaml = yaml.safe_load(black_list_file) - installer_type = ft_constants.CI_INSTALLER_TYPE - deploy_scenario = ft_constants.CI_SCENARIO + installer_type = CONST.INSTALLER_TYPE + deploy_scenario = CONST.DEPLOY_SCENARIO if (bool(installer_type) * bool(deploy_scenario)): if 'scenario' in black_list_yaml.keys(): for item in black_list_yaml['scenario']: diff --git a/functest/opnfv_tests/openstack/snaps/api_check.py b/functest/opnfv_tests/openstack/snaps/api_check.py index e6ee81e9d..17d05b922 100644 --- a/functest/opnfv_tests/openstack/snaps/api_check.py +++ b/functest/opnfv_tests/openstack/snaps/api_check.py @@ -5,11 +5,13 @@ # # http://www.apache.org/licenses/LICENSE-2.0 -import functest.utils.functest_utils as ft_utils +import unittest + +from snaps import test_suite_builder + from functest.core.pytest_suite_runner import PyTestSuiteRunner from functest.opnfv_tests.openstack.snaps import snaps_utils -from snaps import test_suite_builder -import unittest +from functest.utils.constants import CONST class ApiCheck(PyTestSuiteRunner): @@ -22,10 +24,11 @@ class ApiCheck(PyTestSuiteRunner): super(ApiCheck, self).__init__() self.suite = unittest.TestSuite() - creds_file = ft_utils.get_functest_config('general.openstack.creds') - use_key = ft_utils.get_functest_config('snaps.use_keystone') + self.case_name = "api_check" ext_net_name = snaps_utils.get_ext_net_name() - test_suite_builder.add_openstack_api_tests(self.suite, creds_file, - ext_net_name, - use_keystone=use_key) + test_suite_builder.add_openstack_api_tests( + self.suite, + CONST.openstack_creds, + ext_net_name, + use_keystone=CONST.snaps_use_keystone) diff --git a/functest/opnfv_tests/openstack/snaps/connection_check.py b/functest/opnfv_tests/openstack/snaps/connection_check.py index 42e38d67c..11f8ad074 100644 --- a/functest/opnfv_tests/openstack/snaps/connection_check.py +++ b/functest/opnfv_tests/openstack/snaps/connection_check.py @@ -5,11 +5,13 @@ # # http://www.apache.org/licenses/LICENSE-2.0 -import functest.utils.functest_utils as ft_utils +import unittest + +from snaps import test_suite_builder + from functest.core.pytest_suite_runner import PyTestSuiteRunner from functest.opnfv_tests.openstack.snaps import snaps_utils -from snaps import test_suite_builder -import unittest +from functest.utils.constants import CONST class ConnectionCheck(PyTestSuiteRunner): @@ -22,10 +24,11 @@ class ConnectionCheck(PyTestSuiteRunner): super(ConnectionCheck, self).__init__() self.suite = unittest.TestSuite() - creds_file = ft_utils.get_functest_config('general.openstack.creds') - use_key = ft_utils.get_functest_config('snaps.use_keystone') + self.case_name = "connection_check" ext_net_name = snaps_utils.get_ext_net_name() - test_suite_builder.add_openstack_client_tests(self.suite, creds_file, - ext_net_name, - use_keystone=use_key) + test_suite_builder.add_openstack_client_tests( + self.suite, + CONST.openstack_creds, + ext_net_name, + use_keystone=CONST.snaps_use_keystone) diff --git a/functest/opnfv_tests/openstack/snaps/smoke.py b/functest/opnfv_tests/openstack/snaps/smoke.py index 25433a325..83eb6600c 100644 --- a/functest/opnfv_tests/openstack/snaps/smoke.py +++ b/functest/opnfv_tests/openstack/snaps/smoke.py @@ -5,12 +5,14 @@ # # http://www.apache.org/licenses/LICENSE-2.0 -import functest.utils.functest_utils as ft_utils +import os +import unittest + +from snaps import test_suite_builder + from functest.core.pytest_suite_runner import PyTestSuiteRunner from functest.opnfv_tests.openstack.snaps import snaps_utils -from snaps import test_suite_builder -import unittest -import os +from functest.utils.constants import CONST class SnapsSmoke(PyTestSuiteRunner): @@ -23,18 +25,19 @@ class SnapsSmoke(PyTestSuiteRunner): super(SnapsSmoke, self).__init__() self.suite = unittest.TestSuite() - creds_file = ft_utils.get_functest_config('general.openstack.creds') - use_key = ft_utils.get_functest_config('snaps.use_keystone') - use_fip = ft_utils.get_functest_config('snaps.use_floating_ips') + self.case_name = "snaps_smoke" + use_fip = CONST.snaps_use_floating_ips ext_net_name = snaps_utils.get_ext_net_name() # Tests requiring floating IPs leverage files contained within the # SNAPS repository and are found relative to that path if use_fip: - snaps_dir = ft_utils.get_functest_config( - 'general.directories.dir_repo_snaps') + '/snaps' + snaps_dir = CONST.dir_repo_snaps + '/snaps' os.chdir(snaps_dir) test_suite_builder.add_openstack_integration_tests( - self.suite, creds_file, ext_net_name, use_keystone=use_key, + self.suite, + CONST.openstack_creds, + ext_net_name, + use_keystone=CONST.snaps_use_keystone, use_floating_ips=use_fip) diff --git a/functest/opnfv_tests/openstack/snaps/snaps_utils.py b/functest/opnfv_tests/openstack/snaps/snaps_utils.py index a25ad3e0d..4ea1a04ad 100644 --- a/functest/opnfv_tests/openstack/snaps/snaps_utils.py +++ b/functest/opnfv_tests/openstack/snaps/snaps_utils.py @@ -5,17 +5,18 @@ # # http://www.apache.org/licenses/LICENSE-2.0 -import functest.utils.functest_utils as ft_utils from snaps.openstack.tests import openstack_tests from snaps.openstack.utils import neutron_utils +from functest.utils.constants import CONST + def get_ext_net_name(): """ Returns the first external network name :return: """ - os_env_file = ft_utils.get_functest_config('general.openstack.creds') + os_env_file = CONST.openstack_creds os_creds = openstack_tests.get_credentials(os_env_file=os_env_file) neutron = neutron_utils.neutron_client(os_creds) ext_nets = neutron_utils.get_external_networks(neutron) diff --git a/functest/opnfv_tests/openstack/tempest/__init__.py b/functest/opnfv_tests/openstack/tempest/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/__init__.py diff --git a/functest/opnfv_tests/openstack/tempest/conf_utils.py b/functest/opnfv_tests/openstack/tempest/conf_utils.py new file mode 100644 index 000000000..5295ff373 --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/conf_utils.py @@ -0,0 +1,190 @@ +#!/usr/bin/python +# +# Copyright (c) 2015 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +import ConfigParser +import os +import re +import shutil + +import opnfv.utils.constants as releng_constants + +import functest.utils.functest_utils as ft_utils +from functest.utils.constants import CONST + +IMAGE_ID_ALT = None +FLAVOR_ID_ALT = None +REPO_PATH = CONST.dir_repo_functest +GLANCE_IMAGE_PATH = os.path.join(CONST.dir_functest_data, + CONST.openstack_image_file_name) +TEMPEST_TEST_LIST_DIR = CONST.dir_tempest_cases +TEMPEST_RESULTS_DIR = os.path.join(CONST.dir_results, + 'tempest') +TEMPEST_CUSTOM = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, + 'test_list.txt') +TEMPEST_BLACKLIST = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, + 'blacklist.txt') +TEMPEST_DEFCORE = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, + 'defcore_req.txt') +TEMPEST_RAW_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_raw_list.txt') +TEMPEST_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_list.txt') + +CI_INSTALLER_TYPE = CONST.INSTALLER_TYPE +CI_INSTALLER_IP = CONST.INSTALLER_IP + + +def configure_tempest(logger, deployment_dir, IMAGE_ID=None, FLAVOR_ID=None): + """ + Add/update needed parameters into tempest.conf file generated by Rally + """ + tempest_conf_file = deployment_dir + "/tempest.conf" + if os.path.isfile(tempest_conf_file): + logger.debug("Deleting old tempest.conf file...") + os.remove(tempest_conf_file) + + logger.debug("Generating new tempest.conf file...") + cmd = "rally verify genconfig" + ft_utils.execute_command(cmd) + + logger.debug("Finding tempest.conf file...") + if not os.path.isfile(tempest_conf_file): + logger.error("Tempest configuration file %s NOT found." + % tempest_conf_file) + return releng_constants.EXIT_RUN_ERROR + + logger.debug("Updating selected tempest.conf parameters...") + config = ConfigParser.RawConfigParser() + config.read(tempest_conf_file) + config.set( + 'compute', + 'fixed_network_name', + CONST.tempest_private_net_name) + if CONST.tempest_use_custom_images: + if IMAGE_ID is not None: + config.set('compute', 'image_ref', IMAGE_ID) + if IMAGE_ID_ALT is not None: + config.set('compute', 'image_ref_alt', IMAGE_ID_ALT) + if CONST.tempest_use_custom_flavors: + if FLAVOR_ID is not None: + config.set('compute', 'flavor_ref', FLAVOR_ID) + if FLAVOR_ID_ALT is not None: + config.set('compute', 'flavor_ref_alt', FLAVOR_ID_ALT) + config.set('identity', 'tenant_name', CONST.tempest_identity_tenant_name) + config.set('identity', 'username', CONST.tempest_identity_user_name) + config.set('identity', 'password', CONST.tempest_identity_user_password) + config.set( + 'validation', 'ssh_timeout', CONST.tempest_validation_ssh_timeout) + + if CONST.OS_ENDPOINT_TYPE is not None: + services_list = ['compute', + 'volume', + 'image', + 'network', + 'data-processing', + 'object-storage', + 'orchestration'] + sections = config.sections() + for service in services_list: + if service not in sections: + config.add_section(service) + config.set(service, 'endpoint_type', + CONST.OS_ENDPOINT_TYPE) + + with open(tempest_conf_file, 'wb') as config_file: + config.write(config_file) + + # Copy tempest.conf to /home/opnfv/functest/results/tempest/ + shutil.copyfile( + tempest_conf_file, TEMPEST_RESULTS_DIR + '/tempest.conf') + + return releng_constants.EXIT_OK + + +def configure_tempest_multisite(logger, deployment_dir): + """ + Add/update needed parameters into tempest.conf file generated by Rally + """ + logger.debug("configure the tempest") + configure_tempest(logger, deployment_dir) + + logger.debug("Finding tempest.conf file...") + tempest_conf_old = os.path.join(deployment_dir, '/tempest.conf') + if not os.path.isfile(tempest_conf_old): + logger.error("Tempest configuration file %s NOT found." + % tempest_conf_old) + return releng_constants.EXIT_RUN_ERROR + + # Copy tempest.conf to /home/opnfv/functest/results/tempest/ + cur_path = os.path.split(os.path.realpath(__file__))[0] + tempest_conf_file = os.path.join(cur_path, '/tempest_multisite.conf') + shutil.copyfile(tempest_conf_old, tempest_conf_file) + + logger.debug("Updating selected tempest.conf parameters...") + config = ConfigParser.RawConfigParser() + config.read(tempest_conf_file) + + config.set('service_available', 'kingbird', 'true') + cmd = ("openstack endpoint show kingbird | grep publicurl |" + "awk '{print $4}' | awk -F '/' '{print $4}'") + kingbird_api_version = os.popen(cmd).read() + if CI_INSTALLER_TYPE == 'fuel': + # For MOS based setup, the service is accessible + # via bind host + kingbird_conf_path = "/etc/kingbird/kingbird.conf" + installer_type = CI_INSTALLER_TYPE + installer_ip = CI_INSTALLER_IP + installer_username = CONST.__getattribute__( + 'multisite_{}_installer_username'.format(installer_type)) + installer_password = CONST.__getattribute__( + 'multisite_{}_installer_password'.format(installer_type)) + + ssh_options = ("-o UserKnownHostsFile=/dev/null -o " + "StrictHostKeyChecking=no") + + # Get the controller IP from the fuel node + cmd = 'sshpass -p %s ssh 2>/dev/null %s %s@%s \ + \'fuel node --env 1| grep controller | grep "True\| 1" \ + | awk -F\| "{print \$5}"\'' % (installer_password, + ssh_options, + installer_username, + installer_ip) + multisite_controller_ip = "".join(os.popen(cmd).read().split()) + + # Login to controller and get bind host details + cmd = 'sshpass -p %s ssh 2>/dev/null %s %s@%s "ssh %s \\" \ + grep -e "^bind_" %s \\""' % (installer_password, + ssh_options, + installer_username, + installer_ip, + multisite_controller_ip, + kingbird_conf_path) + bind_details = os.popen(cmd).read() + bind_details = "".join(bind_details.split()) + # Extract port number from the bind details + bind_port = re.findall(r"\D(\d{4})", bind_details)[0] + # Extract ip address from the bind details + bind_host = re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", + bind_details)[0] + kingbird_endpoint_url = "http://%s:%s/" % (bind_host, bind_port) + else: + cmd = "openstack endpoint show kingbird | grep publicurl |\ + awk '{print $4}' | awk -F '/' '{print $3}'" + kingbird_endpoint_url = os.popen(cmd).read() + + try: + config.add_section("kingbird") + except Exception: + logger.info('kingbird section exist') + config.set('kingbird', 'endpoint_type', 'publicURL') + config.set('kingbird', 'TIME_TO_SYNC', '20') + config.set('kingbird', 'endpoint_url', kingbird_endpoint_url) + config.set('kingbird', 'api_version', kingbird_api_version) + with open(tempest_conf_file, 'wb') as config_file: + config.write(config_file) + + return releng_constants.EXIT_OK diff --git a/functest/opnfv_tests/openstack/tempest/gen_tempest_conf.py b/functest/opnfv_tests/openstack/tempest/gen_tempest_conf.py deleted file mode 100755 index 1216a671d..000000000 --- a/functest/opnfv_tests/openstack/tempest/gen_tempest_conf.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2015 All rights reserved -# This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Execute Multisite Tempest test cases -## - -import ConfigParser -import os -import re -import shutil -import functest.utils.functest_utils as ft_utils -import functest.utils.functest_logger as ft_logger -from run_tempest import configure_tempest -from run_tempest import TEMPEST_RESULTS_DIR -import functest.utils.functest_constants as ft_constants - -logger = ft_logger.Logger("gen_tempest_conf").getLogger() - -CI_INSTALLER_TYPE = ft_constants.CI_INSTALLER_TYPE -CI_INSTALLER_IP = ft_constants.CI_INSTALLER_IP - - -def configure_tempest_multisite(deployment_dir): - """ - Add/update needed parameters into tempest.conf file generated by Rally - """ - logger.debug("configure the tempest") - configure_tempest(deployment_dir) - - logger.debug("Finding tempest.conf file...") - tempest_conf_old = os.path.join(deployment_dir, '/tempest.conf') - if not os.path.isfile(tempest_conf_old): - logger.error("Tempest configuration file %s NOT found." - % tempest_conf_old) - exit(-1) - - # Copy tempest.conf to /home/opnfv/functest/results/tempest/ - cur_path = os.path.split(os.path.realpath(__file__))[0] - tempest_conf_file = os.path.join(cur_path, '/tempest_multisite.conf') - shutil.copyfile(tempest_conf_old, tempest_conf_file) - - logger.debug("Updating selected tempest.conf parameters...") - config = ConfigParser.RawConfigParser() - config.read(tempest_conf_file) - - config.set('service_available', 'kingbird', 'true') - cmd = ("openstack endpoint show kingbird | grep publicurl |" - "awk '{print $4}' | awk -F '/' '{print $4}'") - kingbird_api_version = os.popen(cmd).read() - if CI_INSTALLER_TYPE == 'fuel': - # For MOS based setup, the service is accessible - # via bind host - kingbird_conf_path = "/etc/kingbird/kingbird.conf" - installer_type = CI_INSTALLER_TYPE - installer_ip = CI_INSTALLER_IP - installer_username = ft_utils.get_functest_config( - "multisite." + installer_type + - "_environment.installer_username") - installer_password = ft_utils.get_functest_config( - "multisite." + installer_type + - "_environment.installer_password") - - ssh_options = ("-o UserKnownHostsFile=/dev/null -o " - "StrictHostKeyChecking=no") - - # Get the controller IP from the fuel node - cmd = 'sshpass -p %s ssh 2>/dev/null %s %s@%s \ - \'fuel node --env 1| grep controller | grep "True\| 1" \ - | awk -F\| "{print \$5}"\'' % (installer_password, - ssh_options, - installer_username, - installer_ip) - multisite_controller_ip = "".join(os.popen(cmd).read().split()) - - # Login to controller and get bind host details - cmd = 'sshpass -p %s ssh 2>/dev/null %s %s@%s "ssh %s \\" \ - grep -e "^bind_" %s \\""' % (installer_password, - ssh_options, - installer_username, - installer_ip, - multisite_controller_ip, - kingbird_conf_path) - bind_details = os.popen(cmd).read() - bind_details = "".join(bind_details.split()) - # Extract port number from the bind details - bind_port = re.findall(r"\D(\d{4})", bind_details)[0] - # Extract ip address from the bind details - bind_host = re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", - bind_details)[0] - kingbird_endpoint_url = "http://%s:%s/" % (bind_host, bind_port) - else: - cmd = "openstack endpoint show kingbird | grep publicurl |\ - awk '{print $4}' | awk -F '/' '{print $3}'" - kingbird_endpoint_url = os.popen(cmd).read() - - try: - config.add_section("kingbird") - except Exception: - logger.info('kingbird section exist') - config.set('kingbird', 'endpoint_type', 'publicURL') - config.set('kingbird', 'TIME_TO_SYNC', '20') - config.set('kingbird', 'endpoint_url', kingbird_endpoint_url) - config.set('kingbird', 'api_version', kingbird_api_version) - with open(tempest_conf_file, 'wb') as config_file: - config.write(config_file) - - return True - - -def main(): - - if not os.path.exists(TEMPEST_RESULTS_DIR): - os.makedirs(TEMPEST_RESULTS_DIR) - - deployment_dir = ft_utils.get_deployment_dir() - configure_tempest_multisite(deployment_dir) - - -if __name__ == '__main__': - main() diff --git a/functest/opnfv_tests/openstack/tempest/run_tempest.py b/functest/opnfv_tests/openstack/tempest/run_tempest.py deleted file mode 100755 index 6406cd193..000000000 --- a/functest/opnfv_tests/openstack/tempest/run_tempest.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/usr/bin/env python -# -# Description: -# Runs tempest and pushes the results to the DB -# -# Authors: -# morgan.richomme@orange.com -# jose.lausuch@ericsson.com -# viktor.tikkanen@nokia.com -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -# -import ConfigParser -import os -import re -import shutil -import subprocess -import sys -import time - -import argparse -import yaml - -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils -import functest.utils.openstack_utils as os_utils -import functest.utils.functest_constants as ft_constants - -modes = ['full', 'smoke', 'baremetal', 'compute', 'data_processing', - 'identity', 'image', 'network', 'object_storage', 'orchestration', - 'telemetry', 'volume', 'custom', 'defcore', 'feature_multisite'] - -""" tests configuration """ -parser = argparse.ArgumentParser() -parser.add_argument("-d", "--debug", - help="Debug mode", - action="store_true") -parser.add_argument("-s", "--serial", - help="Run tests in one thread", - action="store_true") -parser.add_argument("-m", "--mode", - help="Tempest test mode [smoke, all]", - default="smoke") -parser.add_argument("-r", "--report", - help="Create json result file", - action="store_true") -parser.add_argument("-n", "--noclean", - help="Don't clean the created resources for this test.", - action="store_true") -parser.add_argument("-c", "--conf", - help="User-specified Tempest config file location", - default="") - -args = parser.parse_args() - -""" logging configuration """ -logger = ft_logger.Logger("run_tempest").getLogger() - -GLANCE_IMAGE_NAME = ft_constants.GLANCE_IMAGE_NAME -GLANCE_IMAGE_FILENAME = ft_constants.GLANCE_IMAGE_FILENAME -GLANCE_IMAGE_FORMAT = ft_constants.GLANCE_IMAGE_FORMAT -GLANCE_IMAGE_PATH = os.path.join(ft_constants.FUNCTEST_DATA_DIR, - GLANCE_IMAGE_FILENAME) - -IMAGE_ID_ALT = None - -FLAVOR_NAME = ft_constants.FLAVOR_NAME -FLAVOR_RAM = ft_constants.FLAVOR_RAM -FLAVOR_DISK = ft_constants.FLAVOR_DISK -FLAVOR_VCPUS = ft_constants.FLAVOR_VCPUS -FLAVOR_ID_ALT = None - -TEMPEST_PRIVATE_NET_NAME = ft_constants.TEMPEST_PRIVATE_NET_NAME -TEMPEST_PRIVATE_SUBNET_NAME = ft_constants.TEMPEST_PRIVATE_SUBNET_NAME -TEMPEST_PRIVATE_SUBNET_CIDR = ft_constants.TEMPEST_PRIVATE_SUBNET_CIDR -TEMPEST_ROUTER_NAME = ft_constants.TEMPEST_ROUTER_NAME -TEMPEST_TENANT_NAME = ft_constants.TEMPEST_TENANT_NAME -TEMPEST_TENANT_DESCRIPTION = ft_constants.TEMPEST_TENANT_DESCRIPTION -TEMPEST_USER_NAME = ft_constants.TEMPEST_USER_NAME -TEMPEST_USER_PASSWORD = ft_constants.TEMPEST_USER_PASSWORD -TEMPEST_SSH_TIMEOUT = ft_constants.TEMPEST_SSH_TIMEOUT -TEMPEST_USE_CUSTOM_IMAGES = ft_constants.TEMPEST_USE_CUSTOM_IMAGES -TEMPEST_USE_CUSTOM_FLAVORS = ft_constants.TEMPEST_USE_CUSTOM_FLAVORS - -RESULTS_DIR = ft_constants.FUNCTEST_RESULTS_DIR -TEMPEST_RESULTS_DIR = os.path.join(RESULTS_DIR, 'tempest') - -REPO_PATH = ft_constants.FUNCTEST_REPO_DIR -TEMPEST_TEST_LIST_DIR = ft_constants.TEMPEST_TEST_LIST_DIR -TEMPEST_CUSTOM = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, - 'test_list.txt') -TEMPEST_BLACKLIST = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, - 'blacklist.txt') -TEMPEST_DEFCORE = os.path.join(REPO_PATH, TEMPEST_TEST_LIST_DIR, - 'defcore_req.txt') -TEMPEST_RAW_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_raw_list.txt') -TEMPEST_LIST = os.path.join(TEMPEST_RESULTS_DIR, 'test_list.txt') - - -class GlobalVariables: - IMAGE_ID = None - FLAVOR_ID = None - MODE = "smoke" - - -def get_info(file_result): - test_run = "" - duration = "" - test_failed = "" - - p = subprocess.Popen('cat tempest.log', - shell=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - for line in p.stdout.readlines(): - # print line, - if (len(test_run) < 1): - test_run = re.findall("[0-9]*\.[0-9]*s", line) - if (len(duration) < 1): - duration = re.findall("[0-9]*\ tests", line) - regexp = r"(failures=[0-9]+)" - if (len(test_failed) < 1): - test_failed = re.findall(regexp, line) - - logger.debug("test_run:" + test_run) - logger.debug("duration:" + duration) - - -def create_tempest_resources(): - keystone_client = os_utils.get_keystone_client() - - logger.debug("Creating tenant and user for Tempest suite") - tenant_id = os_utils.create_tenant(keystone_client, - TEMPEST_TENANT_NAME, - TEMPEST_TENANT_DESCRIPTION) - if not tenant_id: - logger.error("Error : Failed to create %s tenant" - % TEMPEST_TENANT_NAME) - - user_id = os_utils.create_user(keystone_client, TEMPEST_USER_NAME, - TEMPEST_USER_PASSWORD, - None, tenant_id) - if not user_id: - logger.error("Error : Failed to create %s user" % TEMPEST_USER_NAME) - - logger.debug("Creating private network for Tempest suite") - network_dic = \ - os_utils.create_shared_network_full(TEMPEST_PRIVATE_NET_NAME, - TEMPEST_PRIVATE_SUBNET_NAME, - TEMPEST_ROUTER_NAME, - TEMPEST_PRIVATE_SUBNET_CIDR) - if not network_dic: - exit(1) - - if TEMPEST_USE_CUSTOM_IMAGES: - # adding alternative image should be trivial should we need it - logger.debug("Creating image for Tempest suite") - _, GlobalVariables.IMAGE_ID = os_utils.get_or_create_image( - GLANCE_IMAGE_NAME, GLANCE_IMAGE_PATH, GLANCE_IMAGE_FORMAT) - if not GlobalVariables.IMAGE_ID: - exit(-1) - - if TEMPEST_USE_CUSTOM_FLAVORS: - # adding alternative flavor should be trivial should we need it - logger.debug("Creating flavor for Tempest suite") - _, GlobalVariables.FLAVOR_ID = os_utils.get_or_create_flavor( - FLAVOR_NAME, FLAVOR_RAM, FLAVOR_DISK, FLAVOR_VCPUS) - if not GlobalVariables.FLAVOR_ID: - exit(-1) - - -def configure_tempest(deployment_dir): - """ - Add/update needed parameters into tempest.conf file generated by Rally - """ - - tempest_conf_file = deployment_dir + "/tempest.conf" - if os.path.isfile(tempest_conf_file): - logger.debug("Deleting old tempest.conf file...") - os.remove(tempest_conf_file) - - logger.debug("Generating new tempest.conf file...") - cmd = "rally verify genconfig" - ft_utils.execute_command(cmd) - - logger.debug("Finding tempest.conf file...") - if not os.path.isfile(tempest_conf_file): - logger.error("Tempest configuration file %s NOT found." - % tempest_conf_file) - exit(-1) - - logger.debug("Updating selected tempest.conf parameters...") - config = ConfigParser.RawConfigParser() - config.read(tempest_conf_file) - config.set('compute', 'fixed_network_name', TEMPEST_PRIVATE_NET_NAME) - if TEMPEST_USE_CUSTOM_IMAGES: - if GlobalVariables.IMAGE_ID is not None: - config.set('compute', 'image_ref', GlobalVariables.IMAGE_ID) - if IMAGE_ID_ALT is not None: - config.set('compute', 'image_ref_alt', IMAGE_ID_ALT) - if TEMPEST_USE_CUSTOM_FLAVORS: - if GlobalVariables.FLAVOR_ID is not None: - config.set('compute', 'flavor_ref', GlobalVariables.FLAVOR_ID) - if FLAVOR_ID_ALT is not None: - config.set('compute', 'flavor_ref_alt', FLAVOR_ID_ALT) - config.set('identity', 'tenant_name', TEMPEST_TENANT_NAME) - config.set('identity', 'username', TEMPEST_USER_NAME) - config.set('identity', 'password', TEMPEST_USER_PASSWORD) - config.set('validation', 'ssh_timeout', TEMPEST_SSH_TIMEOUT) - - if ft_constants.OS_ENDPOINT_TYPE is not None: - services_list = ['compute', 'volume', 'image', 'network', - 'data-processing', 'object-storage', 'orchestration'] - sections = config.sections() - for service in services_list: - if service not in sections: - config.add_section(service) - config.set(service, 'endpoint_type', - ft_constants.OS_ENDPOINT_TYPE) - - with open(tempest_conf_file, 'wb') as config_file: - config.write(config_file) - - # Copy tempest.conf to /home/opnfv/functest/results/tempest/ - shutil.copyfile(tempest_conf_file, TEMPEST_RESULTS_DIR + '/tempest.conf') - return True - - -def read_file(filename): - with open(filename) as src: - return [line.strip() for line in src.readlines()] - - -def generate_test_list(deployment_dir, mode): - logger.debug("Generating test case list...") - if mode == 'defcore': - shutil.copyfile(TEMPEST_DEFCORE, TEMPEST_RAW_LIST) - elif mode == 'custom': - if os.path.isfile(TEMPEST_CUSTOM): - shutil.copyfile(TEMPEST_CUSTOM, TEMPEST_RAW_LIST) - else: - logger.error("Tempest test list file %s NOT found." - % TEMPEST_CUSTOM) - exit(-1) - else: - if mode == 'smoke': - testr_mode = "smoke" - elif mode == 'feature_multisite': - testr_mode = " | grep -i kingbird " - elif mode == 'full': - testr_mode = "" - else: - testr_mode = 'tempest.api.' + mode - cmd = ("cd " + deployment_dir + ";" + "testr list-tests " + - testr_mode + ">" + TEMPEST_RAW_LIST + ";cd") - ft_utils.execute_command(cmd) - - -def apply_tempest_blacklist(): - logger.debug("Applying tempest blacklist...") - cases_file = read_file(TEMPEST_RAW_LIST) - result_file = open(TEMPEST_LIST, 'w') - black_tests = [] - try: - installer_type = ft_constants.CI_INSTALLER_TYPE - deploy_scenario = ft_constants.CI_SCENARIO - if (bool(installer_type) * bool(deploy_scenario)): - # if INSTALLER_TYPE and DEPLOY_SCENARIO are set we read the file - black_list_file = open(TEMPEST_BLACKLIST) - black_list_yaml = yaml.safe_load(black_list_file) - black_list_file.close() - for item in black_list_yaml: - scenarios = item['scenarios'] - installers = item['installers'] - if (deploy_scenario in scenarios and - installer_type in installers): - tests = item['tests'] - for test in tests: - black_tests.append(test) - break - except: - black_tests = [] - logger.debug("Tempest blacklist file does not exist.") - - for cases_line in cases_file: - for black_tests_line in black_tests: - if black_tests_line in cases_line: - break - else: - result_file.write(str(cases_line) + '\n') - result_file.close() - - -def run_tempest(OPTION): - # - # the "main" function of the script which launches Rally to run Tempest - # :param option: tempest option (smoke, ..) - # :return: void - # - logger.info("Starting Tempest test suite: '%s'." % OPTION) - start_time = time.time() - stop_time = start_time - cmd_line = "rally verify start " + OPTION + " --system-wide" - - header = ("Tempest environment:\n" - " Installer: %s\n Scenario: %s\n Node: %s\n Date: %s\n" % - (ft_constants.CI_INSTALLER_TYPE, - ft_constants.CI_SCENARIO, - ft_constants.CI_NODE, - time.strftime("%a %b %d %H:%M:%S %Z %Y"))) - - f_stdout = open(TEMPEST_RESULTS_DIR + "/tempest.log", 'w+') - f_stderr = open(TEMPEST_RESULTS_DIR + "/tempest-error.log", 'w+') - f_env = open(TEMPEST_RESULTS_DIR + "/environment.log", 'w+') - f_env.write(header) - - # subprocess.call(cmd_line, shell=True, stdout=f_stdout, stderr=f_stderr) - p = subprocess.Popen( - cmd_line, shell=True, - stdout=subprocess.PIPE, - stderr=f_stderr, - bufsize=1) - - with p.stdout: - for line in iter(p.stdout.readline, b''): - if re.search("\} tempest\.", line): - logger.info(line.replace('\n', '')) - f_stdout.write(line) - p.wait() - - f_stdout.close() - f_stderr.close() - f_env.close() - - cmd_line = "rally verify show" - output = "" - p = subprocess.Popen( - cmd_line, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - for line in p.stdout: - if re.search("Tests\:", line): - break - output += line - logger.info(output) - - cmd_line = "rally verify list" - cmd = os.popen(cmd_line) - output = (((cmd.read()).splitlines()[-2]).replace(" ", "")).split("|") - # Format: - # | UUID | Deployment UUID | smoke | tests | failures | Created at | - # Duration | Status | - num_tests = output[4] - num_failures = output[5] - time_start = output[6] - duration = output[7] - # Compute duration (lets assume it does not take more than 60 min) - dur_min = int(duration.split(':')[1]) - dur_sec_float = float(duration.split(':')[2]) - dur_sec_int = int(round(dur_sec_float, 0)) - dur_sec_int = dur_sec_int + 60 * dur_min - stop_time = time.time() - - try: - diff = (int(num_tests) - int(num_failures)) - success_rate = 100 * diff / int(num_tests) - except: - success_rate = 0 - - if 'smoke' in args.mode: - case_name = 'tempest_smoke_serial' - elif 'feature' in args.mode: - case_name = args.mode.replace("feature_", "") - else: - case_name = 'tempest_full_parallel' - - status = ft_utils.check_success_rate(case_name, success_rate) - logger.info("Tempest %s success_rate is %s%%, is marked as %s" - % (case_name, success_rate, status)) - - # Push results in payload of testcase - if args.report: - # add the test in error in the details sections - # should be possible to do it during the test - logger.debug("Pushing tempest results into DB...") - with open(TEMPEST_RESULTS_DIR + "/tempest.log", 'r') as myfile: - output = myfile.read() - error_logs = "" - - for match in re.findall('(.*?)[. ]*FAILED', output): - error_logs += match - - # Generate json results for DB - json_results = {"timestart": time_start, "duration": dur_sec_int, - "tests": int(num_tests), "failures": int(num_failures), - "errors": error_logs} - logger.info("Results: " + str(json_results)) - # split Tempest smoke and full - - try: - ft_utils.push_results_to_db("functest", - case_name, - start_time, - stop_time, - status, - json_results) - except: - logger.error("Error pushing results into Database '%s'" - % sys.exc_info()[0]) - - if status == "PASS": - return 0 - else: - return -1 - - -def main(): - - if not (args.mode in modes): - logger.error("Tempest mode not valid. " - "Possible values are:\n" + str(modes)) - exit(-1) - - if not os.path.exists(TEMPEST_RESULTS_DIR): - os.makedirs(TEMPEST_RESULTS_DIR) - - deployment_dir = ft_utils.get_deployment_dir() - create_tempest_resources() - - if "" == args.conf: - GlobalVariables.MODE = "" - configure_tempest(deployment_dir) - else: - GlobalVariables.MODE = " --tempest-config " + args.conf - - generate_test_list(deployment_dir, args.mode) - apply_tempest_blacklist() - - GlobalVariables.MODE += " --tests-file " + TEMPEST_LIST - if args.serial: - GlobalVariables.MODE += " --concur 1" - - ret_val = run_tempest(GlobalVariables.MODE) - if ret_val != 0: - sys.exit(-1) - - sys.exit(0) - - -if __name__ == '__main__': - main() diff --git a/functest/opnfv_tests/openstack/tempest/tempest.py b/functest/opnfv_tests/openstack/tempest/tempest.py new file mode 100644 index 000000000..20b1ebb4c --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/tempest.py @@ -0,0 +1,331 @@ +#!/usr/bin/python +# +# Copyright (c) 2015 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +import os +import re +import shutil +import subprocess +import time + +import yaml + +import conf_utils +import functest.core.testcase_base as testcase_base +from functest.utils.constants import CONST +import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils +import functest.utils.openstack_utils as os_utils + +""" logging configuration """ +logger = ft_logger.Logger("Tempest").getLogger() + + +class TempestCommon(testcase_base.TestcaseBase): + + def __init__(self): + super(TempestCommon, self).__init__() + self.MODE = "" + self.OPTION = "" + self.FLAVOR_ID = None + self.IMAGE_ID = None + self.DEPLOYMENT_DIR = self.get_deployment_dir() + + @staticmethod + def get_deployment_dir(): + """ + Returns current Rally deployment directory + """ + cmd = ("rally deployment list | awk '/" + + CONST.rally_deployment_name + + "/ {print $2}'") + p = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + deployment_uuid = p.stdout.readline().rstrip() + if deployment_uuid == "": + logger.error("Rally deployment not found.") + exit(-1) + return os.path.join(CONST.dir_rally_inst, + "tempest/for-deployment-" + deployment_uuid) + + @staticmethod + def read_file(filename): + with open(filename) as src: + return [line.strip() for line in src.readlines()] + + def create_tempest_resources(self): + keystone_client = os_utils.get_keystone_client() + + logger.debug("Creating tenant and user for Tempest suite") + tenant_id = os_utils.create_tenant( + keystone_client, + CONST.tempest_identity_tenant_name, + CONST.tempest_identity_tenant_description) + if not tenant_id: + logger.error("Error : Failed to create %s tenant" + % CONST.tempest_identity_tenant_name) + + user_id = os_utils.create_user(keystone_client, + CONST.tempest_identity_user_name, + CONST.tempest_identity_user_password, + None, tenant_id) + if not user_id: + logger.error("Error : Failed to create %s user" % + CONST.tempest_identity_user_name) + + logger.debug("Creating private network for Tempest suite") + network_dic = \ + os_utils.create_shared_network_full( + CONST.tempest_private_net_name, + CONST.tempest_private_subnet_name, + CONST.tempest_router_name, + CONST.tempest_private_subnet_cidr) + if not network_dic: + return testcase_base.TestcaseBase.EX_RUN_ERROR + + if CONST.tempest_use_custom_images: + # adding alternative image should be trivial should we need it + logger.debug("Creating image for Tempest suite") + _, self.IMAGE_ID = os_utils.get_or_create_image( + CONST.openstack_image_name, conf_utils.GLANCE_IMAGE_PATH, + CONST.openstack_image_disk_format) + if not self.IMAGE_ID: + return testcase_base.TestcaseBase.EX_RUN_ERROR + + if CONST.tempest_use_custom_flavors: + # adding alternative flavor should be trivial should we need it + logger.debug("Creating flavor for Tempest suite") + _, self.FLAVOR_ID = os_utils.get_or_create_flavor( + CONST.openstack_flavor_name, + CONST.openstack_flavor_ram, + CONST.openstack_flavor_disk, + CONST.openstack_flavor_vcpus) + if not self.FLAVOR_ID: + return testcase_base.TestcaseBase.EX_RUN_ERROR + + return testcase_base.TestcaseBase.EX_OK + + def generate_test_list(self, DEPLOYMENT_DIR): + logger.debug("Generating test case list...") + if self.MODE == 'defcore': + shutil.copyfile( + conf_utils.TEMPEST_DEFCORE, conf_utils.TEMPEST_RAW_LIST) + elif self.MODE == 'custom': + if os.path.isfile(conf_utils.TEMPEST_CUSTOM): + shutil.copyfile( + conf_utils.TEMPEST_CUSTOM, conf_utils.TEMPEST_RAW_LIST) + else: + logger.error("Tempest test list file %s NOT found." + % conf_utils.TEMPEST_CUSTOM) + return testcase_base.TestcaseBase.EX_RUN_ERROR + else: + if self.MODE == 'smoke': + testr_mode = "smoke" + elif self.MODE == 'feature_multisite': + testr_mode = " | grep -i kingbird " + elif self.MODE == 'full': + testr_mode = "" + else: + testr_mode = 'tempest.api.' + self.MODE + cmd = ("cd " + DEPLOYMENT_DIR + ";" + "testr list-tests " + + testr_mode + ">" + conf_utils.TEMPEST_RAW_LIST + ";cd") + ft_utils.execute_command(cmd) + + return testcase_base.TestcaseBase.EX_OK + + def apply_tempest_blacklist(self): + logger.debug("Applying tempest blacklist...") + cases_file = self.read_file(conf_utils.TEMPEST_RAW_LIST) + result_file = open(conf_utils.TEMPEST_LIST, 'w') + black_tests = [] + try: + installer_type = CONST.INSTALLER_TYPE + deploy_scenario = CONST.DEPLOY_SCENARIO + if (bool(installer_type) * bool(deploy_scenario)): + # if INSTALLER_TYPE and DEPLOY_SCENARIO are set we read the + # file + black_list_file = open(conf_utils.TEMPEST_BLACKLIST) + black_list_yaml = yaml.safe_load(black_list_file) + black_list_file.close() + for item in black_list_yaml: + scenarios = item['scenarios'] + installers = item['installers'] + if (deploy_scenario in scenarios and + installer_type in installers): + tests = item['tests'] + for test in tests: + black_tests.append(test) + break + except: + black_tests = [] + logger.debug("Tempest blacklist file does not exist.") + + for cases_line in cases_file: + for black_tests_line in black_tests: + if black_tests_line in cases_line: + break + else: + result_file.write(str(cases_line) + '\n') + result_file.close() + return testcase_base.TestcaseBase.EX_OK + + def run(self): + + self.start_time = time.time() + + if not os.path.exists(conf_utils.TEMPEST_RESULTS_DIR): + os.makedirs(conf_utils.TEMPEST_RESULTS_DIR) + + # Pre-configuration + res = self.create_tempest_resources() + if res != testcase_base.TestcaseBase.EX_OK: + return res + + res = conf_utils.configure_tempest(logger, + self.DEPLOYMENT_DIR, + self.IMAGE_ID, + self.FLAVOR_ID) + if res != testcase_base.TestcaseBase.EX_OK: + return res + + res = self.generate_test_list(self.DEPLOYMENT_DIR) + if res != testcase_base.TestcaseBase.EX_OK: + return res + + res = self.apply_tempest_blacklist() + if res != testcase_base.TestcaseBase.EX_OK: + return res + + self.OPTION += (" --tests-file %s " % conf_utils.TEMPEST_LIST) + + cmd_line = "rally verify start " + self.OPTION + " --system-wide" + logger.info("Starting Tempest test suite: '%s'." % cmd_line) + + header = ("Tempest environment:\n" + " Installer: %s\n Scenario: %s\n Node: %s\n Date: %s\n" % + (CONST.INSTALLER_TYPE, + CONST.DEPLOY_SCENARIO, + CONST.NODE_NAME, + time.strftime("%a %b %d %H:%M:%S %Z %Y"))) + + f_stdout = open(conf_utils.TEMPEST_RESULTS_DIR + "/tempest.log", 'w+') + f_stderr = open( + conf_utils.TEMPEST_RESULTS_DIR + "/tempest-error.log", 'w+') + f_env = open(conf_utils.TEMPEST_RESULTS_DIR + "/environment.log", 'w+') + f_env.write(header) + + # subprocess.call(cmd_line, shell=True, + # stdout=f_stdout, stderr=f_stderr) + p = subprocess.Popen( + cmd_line, shell=True, + stdout=subprocess.PIPE, + stderr=f_stderr, + bufsize=1) + + with p.stdout: + for line in iter(p.stdout.readline, b''): + if re.search("\} tempest\.", line): + logger.info(line.replace('\n', '')) + f_stdout.write(line) + p.wait() + + f_stdout.close() + f_stderr.close() + f_env.close() + + cmd_line = "rally verify show" + output = "" + p = subprocess.Popen(cmd_line, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + for line in p.stdout: + if re.search("Tests\:", line): + break + output += line + logger.info(output) + + cmd_line = "rally verify list" + cmd = os.popen(cmd_line) + output = (((cmd.read()).splitlines()[-2]).replace(" ", "")).split("|") + # Format: + # | UUID | Deployment UUID | smoke | tests | failures | Created at | + # Duration | Status | + num_tests = output[4] + num_failures = output[5] + duration = output[7] + # Compute duration (lets assume it does not take more than 60 min) + dur_min = int(duration.split(':')[1]) + dur_sec_float = float(duration.split(':')[2]) + dur_sec_int = int(round(dur_sec_float, 0)) + dur_sec_int = dur_sec_int + 60 * dur_min + + try: + diff = (int(num_tests) - int(num_failures)) + success_rate = 100 * diff / int(num_tests) + except: + success_rate = 0 + + self.criteria = ft_utils.check_success_rate( + self.case_name, success_rate) + logger.info("Tempest %s success_rate is %s%%, is marked as %s" + % (self.case_name, success_rate, self.criteria)) + + self.stop_time = time.time() + + if self.criteria == "PASS": + return testcase_base.TestcaseBase.EX_OK + else: + return testcase_base.TestcaseBase.EX_TESTCASE_FAILED + + +class TempestSmokeSerial(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_smoke_serial" + self.MODE = "smoke" + self.OPTION = "--concur 1" + + +class TempestSmokeParallel(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_smoke_parallel" + self.MODE = "smoke" + self.OPTION = "" + + +class TempestFullParallel(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_full_parallel" + self.MODE = "full" + + +class TempestMultisite(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "multisite" + self.MODE = "feature_multisite" + self.OPTION = "--concur 1" + conf_utils.configure_tempest_multisite(logger, self.DEPLOYMENT_DIR) + + +class TempestCustom(TempestCommon): + + def __init__(self, mode, option): + TempestCommon.__init__(self) + self.case_name = "tempest_custom" + self.MODE = mode + self.OPTION = option diff --git a/functest/opnfv_tests/openstack/vping/vping_base.py b/functest/opnfv_tests/openstack/vping/vping_base.py index 213b79fcf..a5309bd44 100644..100755 --- a/functest/opnfv_tests/openstack/vping/vping_base.py +++ b/functest/opnfv_tests/openstack/vping/vping_base.py @@ -12,43 +12,38 @@ import pprint import time from datetime import datetime -import functest.utils.functest_utils as ft_utils -import functest.utils.functest_constants as ft_constants -import functest.utils.openstack_utils as os_utils import functest.core.testcase_base as testcase_base +import functest.utils.openstack_utils as os_utils +from functest.utils.constants import CONST class VPingBase(testcase_base.TestcaseBase): def __init__(self): - def get_conf(parameter): - return ft_utils.get_functest_config(parameter) - super(VPingBase, self).__init__() self.logger = None - self.functest_repo = ft_constants.FUNCTEST_REPO_DIR - self.repo = get_conf('general.directories.dir_vping') - self.vm1_name = get_conf('vping.vm_name_1') - self.vm2_name = get_conf('vping.vm_name_2') + self.functest_repo = CONST.dir_repo_functest + self.repo = CONST.dir_vping + self.vm1_name = CONST.vping_vm_name_1 + self.vm2_name = CONST.vping_vm_name_2 self.vm_boot_timeout = 180 self.vm_delete_timeout = 100 - self.ping_timeout = get_conf('vping.ping_timeout') + self.ping_timeout = CONST.vping_ping_timeout - self.image_name = get_conf('vping.image_name') - self.image_filename = get_conf('general.openstack.image_file_name') - self.image_format = get_conf('general.openstack.image_disk_format') - self.image_path = \ - "%s/%s" % (get_conf('general.directories.dir_functest_data'), - self.image_filename) + self.image_name = CONST.vping_image_name + self.image_filename = CONST.openstack_image_file_name + self.image_format = CONST.openstack_image_disk_format + self.image_path = os.path.join(CONST.dir_functest_data, + self.image_filename) - self.flavor_name = get_conf('vping.vm_flavor') + self.flavor_name = CONST.vping_vm_flavor # NEUTRON Private Network parameters - self.private_net_name = get_conf('vping.vping_private_net_name') - self.private_subnet_name = get_conf('vping.vping_private_subnet_name') - self.private_subnet_cidr = get_conf('vping.vping_private_subnet_cidr') - self.router_name = get_conf('vping.vping_router_name') - self.sg_name = get_conf('vping.vping_sg_name') - self.sg_desc = get_conf('vping.vping_sg_descr') + self.private_net_name = CONST.vping_private_net_name + self.private_subnet_name = CONST.vping_private_subnet_name + self.private_subnet_cidr = CONST.vping_private_subnet_cidr + self.router_name = CONST.vping_router_name + self.sg_name = CONST.vping_sg_name + self.sg_desc = CONST.vping_sg_desc self.neutron_client = os_utils.get_neutron_client() self.glance_client = os_utils.get_glance_client() self.nova_client = os_utils.get_nova_client() diff --git a/functest/opnfv_tests/openstack/vping/vping_ssh.py b/functest/opnfv_tests/openstack/vping/vping_ssh.py index 8ae590eda..b032c3087 100644..100755 --- a/functest/opnfv_tests/openstack/vping/vping_ssh.py +++ b/functest/opnfv_tests/openstack/vping/vping_ssh.py @@ -101,9 +101,9 @@ class VPingSSH(vping_base.VPingBase): "from the dhcp agent." % self.vm2_name) # if dhcp not work,it shows "No lease, failing".The test will fail - if "No lease, failing" in console_log \ - and not nolease \ - and not got_ip: + if ("No lease, failing" in console_log and + not nolease and + not got_ip): nolease = True self.logger.debug("Console-log '%s': No lease, failing..." % self.vm2_name) diff --git a/functest/opnfv_tests/openstack/vping/vping_userdata.py b/functest/opnfv_tests/openstack/vping/vping_userdata.py index fa91c12a6..fa91c12a6 100644..100755 --- a/functest/opnfv_tests/openstack/vping/vping_userdata.py +++ b/functest/opnfv_tests/openstack/vping/vping_userdata.py diff --git a/functest/opnfv_tests/sdn/odl/odl.py b/functest/opnfv_tests/sdn/odl/odl.py index 95440746c..45b313d52 100755 --- a/functest/opnfv_tests/sdn/odl/odl.py +++ b/functest/opnfv_tests/sdn/odl/odl.py @@ -15,18 +15,17 @@ import re import sys import urlparse -from robot.api import ExecutionResult, ResultVisitor +import robot.api from robot.errors import RobotError import robot.run from robot.utils.robottime import timestamp_to_secs -import functest.core.testcase_base as testcase_base +from functest.core import testcase_base import functest.utils.functest_logger as ft_logger import functest.utils.openstack_utils as op_utils -import functest.utils.functest_constants as ft_constants -class ODLResultVisitor(ResultVisitor): +class ODLResultVisitor(robot.api.ResultVisitor): def __init__(self): self._data = [] @@ -36,7 +35,7 @@ class ODLResultVisitor(ResultVisitor): output['name'] = test.name output['parent'] = test.parent.name output['status'] = test.status - output['startime'] = test.starttime + output['starttime'] = test.starttime output['endtime'] = test.endtime output['critical'] = test.critical output['text'] = test.message @@ -49,17 +48,17 @@ class ODLResultVisitor(ResultVisitor): class ODLTests(testcase_base.TestcaseBase): - repos = ft_constants.REPOS_DIR + repos = "/home/opnfv/repos/" odl_test_repo = os.path.join(repos, "odl_test") neutron_suite_dir = os.path.join(odl_test_repo, "csit/suites/openstack/neutron") basic_suite_dir = os.path.join(odl_test_repo, "csit/suites/integration/basic") - res_dir = os.path.join(ft_constants.FUNCTEST_RESULTS_DIR, "odl") - + res_dir = '/home/opnfv/functest/results/odl/' logger = ft_logger.Logger("opendaylight").getLogger() def __init__(self): + testcase_base.TestcaseBase.__init__(self) self.case_name = "odl" @classmethod @@ -79,8 +78,8 @@ class ODLTests(testcase_base.TestcaseBase): return False def parse_results(self): - output_dir = os.path.join(self.res_dir, 'output.xml') - result = ExecutionResult(output_dir) + xml_file = os.path.join(self.res_dir, 'output.xml') + result = robot.api.ExecutionResult(xml_file) visitor = ODLResultVisitor() result.visit(visitor) self.criteria = result.suite.status @@ -89,7 +88,6 @@ class ODLTests(testcase_base.TestcaseBase): self.details = {} self.details['description'] = result.suite.name self.details['tests'] = visitor.get_data() - return self.criteria def main(self, **kwargs): dirs = [self.basic_suite_dir, self.neutron_suite_dir] @@ -128,10 +126,8 @@ class ODLTests(testcase_base.TestcaseBase): self.logger.info("\n" + stdout.read()) self.logger.info("ODL results were successfully generated") try: - test_res = self.parse_results() + self.parse_results() self.logger.info("ODL results were successfully parsed") - if test_res is not "PASS": - return self.EX_RUN_ERROR except RobotError as e: self.logger.error("Run tests before publishing: %s" % e.message) @@ -146,11 +142,8 @@ class ODLTests(testcase_base.TestcaseBase): def run(self): try: - kclient = op_utils.get_keystone_client() - keystone_url = kclient.service_catalog.url_for( - service_type='identity', endpoint_type='publicURL') - neutron_url = kclient.service_catalog.url_for( - service_type='network', endpoint_type='publicURL') + keystone_url = op_utils.get_endpoint(service_type='identity') + neutron_url = op_utils.get_endpoint(service_type='network') kwargs = {'keystoneip': urlparse.urlparse(keystone_url).hostname} kwargs['neutronip'] = urlparse.urlparse(neutron_url).hostname kwargs['odlip'] = kwargs['neutronip'] @@ -158,29 +151,23 @@ class ODLTests(testcase_base.TestcaseBase): kwargs['odlrestconfport'] = '8181' kwargs['odlusername'] = 'admin' kwargs['odlpassword'] = 'admin' - - installer_type = ft_constants.CI_INSTALLER_TYPE - kwargs['osusername'] = ft_constants.OS_USERNAME - kwargs['ostenantname'] = ft_constants.OS_TENANT_NAME - kwargs['ospassword'] = ft_constants.OS_PASSWORD - + installer_type = None + if 'INSTALLER_TYPE' in os.environ: + installer_type = os.environ['INSTALLER_TYPE'] + kwargs['osusername'] = os.environ['OS_USERNAME'] + kwargs['ostenantname'] = os.environ['OS_TENANT_NAME'] + kwargs['ospassword'] = os.environ['OS_PASSWORD'] if installer_type == 'fuel': kwargs['odlwebport'] = '8282' elif installer_type == 'apex': - if ft_constants.SDN_CONTROLLER_IP is None: - return self.EX_RUN_ERROR - kwargs['odlip'] = ft_constants.SDN_CONTROLLER_IP + kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP'] kwargs['odlwebport'] = '8181' elif installer_type == 'joid': - if ft_constants.SDN_CONTROLLER is None: - return self.EX_RUN_ERROR - kwargs['odlip'] = ft_constants.SDN_CONTROLLER + kwargs['odlip'] = os.environ['SDN_CONTROLLER'] elif installer_type == 'compass': kwargs['odlwebport'] = '8181' else: - if ft_constants.SDN_CONTROLLER_IP is None: - return self.EX_RUN_ERROR - kwargs['odlip'] = ft_constants.SDN_CONTROLLER_IP + kwargs['odlip'] = os.environ['SDN_CONTROLLER_IP'] except KeyError as e: self.logger.error("Cannot run ODL testcases. " "Please check env var: " @@ -193,44 +180,52 @@ class ODLTests(testcase_base.TestcaseBase): return self.main(**kwargs) +class ODLParser(): + + def __init__(self): + self.parser = argparse.ArgumentParser() + self.parser.add_argument( + '-k', '--keystoneip', help='Keystone IP', + default='127.0.0.1') + self.parser.add_argument( + '-n', '--neutronip', help='Neutron IP', + default='127.0.0.1') + self.parser.add_argument( + '-a', '--osusername', help='Username for OpenStack', + default='admin') + self.parser.add_argument( + '-b', '--ostenantname', help='Tenantname for OpenStack', + default='admin') + self.parser.add_argument( + '-c', '--ospassword', help='Password for OpenStack', + default='admin') + self.parser.add_argument( + '-o', '--odlip', help='OpenDaylight IP', + default='127.0.0.1') + self.parser.add_argument( + '-w', '--odlwebport', help='OpenDaylight Web Portal Port', + default='8080') + self.parser.add_argument( + '-r', '--odlrestconfport', help='OpenDaylight RESTConf Port', + default='8181') + self.parser.add_argument( + '-d', '--odlusername', help='Username for ODL', + default='admin') + self.parser.add_argument( + '-e', '--odlpassword', help='Password for ODL', + default='admin') + self.parser.add_argument( + '-p', '--pushtodb', help='Push results to DB', + action='store_true') + + def parse_args(self, argv=[]): + return vars(self.parser.parse_args(argv)) + + if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('-k', '--keystoneip', - help='Keystone IP', - default='127.0.0.1') - parser.add_argument('-n', '--neutronip', - help='Neutron IP', - default='127.0.0.1') - parser.add_argument('-a', '--osusername', - help='Username for OpenStack', - default='admin') - parser.add_argument('-b', '--ostenantname', - help='Tenantname for OpenStack', - default='admin') - parser.add_argument('-c', '--ospassword', - help='Password for OpenStack', - default='admin') - parser.add_argument('-o', '--odlip', - help='OpenDaylight IP', - default='127.0.0.1') - parser.add_argument('-w', '--odlwebport', - help='OpenDaylight Web Portal Port', - default='8080') - parser.add_argument('-r', '--odlrestconfport', - help='OpenDaylight RESTConf Port', - default='8181') - parser.add_argument('-d', '--odlusername', - help='Username for ODL', - default='admin') - parser.add_argument('-e', '--odlpassword', - help='Password for ODL', - default='admin') - parser.add_argument('-p', '--pushtodb', - help='Push results to DB', - action='store_true') - - args = vars(parser.parse_args()) odl = ODLTests() + parser = ODLParser() + args = parser.parse_args(sys.argv[1:]) try: result = odl.main(**args) if result != testcase_base.TestcaseBase.EX_OK: diff --git a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py index 8ca32e9bb..349b42a88 100644 --- a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py +++ b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py @@ -1,4 +1,4 @@ -import os +import os import re import time import json diff --git a/functest/opnfv_tests/vnf/ims/vims.py b/functest/opnfv_tests/vnf/ims/vims.py index fe888b698..15981f512 100755 --- a/functest/opnfv_tests/vnf/ims/vims.py +++ b/functest/opnfv_tests/vnf/ims/vims.py @@ -19,10 +19,7 @@ import subprocess import time import argparse -import keystoneclient.v2_0.client as ksclient -import novaclient.client as nvclient import requests -from neutronclient.v2_0 import client as ntclient import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils @@ -242,17 +239,15 @@ def main(): if not os.path.exists(VIMS_DATA_DIR): os.makedirs(VIMS_DATA_DIR) - ks_creds = os_utils.get_credentials("keystone") - nv_creds = os_utils.get_credentials("nova") - nt_creds = os_utils.get_credentials("neutron") + creds = os_utils.get_credentials() logger.info("Prepare OpenStack plateform (create tenant and user)") - keystone = ksclient.Client(**ks_creds) + keystone = os_utils.get_keystone_client() - user_id = os_utils.get_user_id(keystone, ks_creds['username']) + user_id = os_utils.get_user_id(keystone, creds['username']) if user_id == '': step_failure("init", "Error : Failed to get id of " + - ks_creds['username']) + creds['username']) tenant_id = os_utils.create_tenant( keystone, VIMS_TENANT_NAME, VIMS_TENANT_DESCRIPTION) @@ -271,7 +266,7 @@ def main(): if not os_utils.add_role_user(keystone, user_id, role_id, tenant_id): logger.error("Error : Failed to add %s on tenant" % - ks_creds['username']) + creds['username']) user_id = os_utils.create_user( keystone, VIMS_TENANT_NAME, VIMS_TENANT_NAME, None, tenant_id) @@ -279,18 +274,10 @@ def main(): logger.error("Error : Failed to create %s user" % VIMS_TENANT_NAME) logger.info("Update OpenStack creds informations") - ks_creds.update({ + creds.update({ "username": VIMS_TENANT_NAME, "password": VIMS_TENANT_NAME, - "tenant_name": VIMS_TENANT_NAME, - }) - - nt_creds.update({ - "tenant_name": VIMS_TENANT_NAME, - }) - - nv_creds.update({ - "project_id": VIMS_TENANT_NAME, + "tenant": VIMS_TENANT_NAME, }) logger.info("Upload some OS images if it doesn't exist") @@ -314,10 +301,8 @@ def main(): "Error : Failed to find or upload required OS " "image for this deployment") - nova = nvclient.Client("2", **nv_creds) - logger.info("Update security group quota for this tenant") - neutron = ntclient.Client(**nt_creds) + neutron = os_utils.get_neutron_client(creds) if not os_utils.update_sg_quota(neutron, tenant_id, 50, 100): step_failure( "init", @@ -325,17 +310,22 @@ def main(): VIMS_TENANT_NAME) # ############### CLOUDIFY INITIALISATION ################ - public_auth_url = keystone.service_catalog.url_for( - service_type='identity', endpoint_type='publicURL') + public_auth_url = os_utils.get_endpoint('identity') cfy = Orchestrator(VIMS_DATA_DIR, CFY_INPUTS) - cfy.set_credentials(username=ks_creds['username'], password=ks_creds[ - 'password'], tenant_name=ks_creds['tenant_name'], + if 'tenant_name' in creds.keys(): + tenant_name = creds['tenant_name'] + elif 'project_name' in creds.keys(): + tenant_name = creds['project_name'] + + cfy.set_credentials(username=creds['username'], + password=creds['password'], + tenant_name=tenant_name, auth_url=public_auth_url) logger.info("Collect flavor id for cloudify manager server") - nova = nvclient.Client("2", **nv_creds) + nova = os_utils.get_nova_client(creds) flavor_name = "m1.large" flavor_id = os_utils.get_flavor_id(nova, flavor_name) @@ -416,7 +406,6 @@ def main(): cw = Clearwater(CW_INPUTS, cfy, logger) logger.info("Collect flavor id for all clearwater vm") - nova = nvclient.Client("2", **nv_creds) flavor_name = "m1.small" flavor_id = os_utils.get_flavor_id(nova, flavor_name) @@ -490,10 +479,6 @@ def main(): if args.noclean: exit(0) - ks_creds = os_utils.get_credentials("keystone") - - keystone = ksclient.Client(**ks_creds) - logger.info("Removing %s tenant .." % CFY_INPUTS['keystone_tenant_name']) tenant_id = os_utils.get_tenant_id( keystone, CFY_INPUTS['keystone_tenant_name']) diff --git a/functest/tests/unit/cli/__init__.py b/functest/tests/unit/cli/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/functest/tests/unit/cli/__init__.py diff --git a/functest/tests/unit/cli/commands/__init__.py b/functest/tests/unit/cli/commands/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/functest/tests/unit/cli/commands/__init__.py diff --git a/functest/tests/unit/cli/commands/test_cli_env.py b/functest/tests/unit/cli/commands/test_cli_env.py new file mode 100644 index 000000000..f70761dcd --- /dev/null +++ b/functest/tests/unit/cli/commands/test_cli_env.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import logging +import unittest + +from git.exc import NoSuchPathError +import mock + +mock.patch('logging.FileHandler').start() # noqa +from functest.cli.commands import cli_env +from functest.utils.constants import CONST +from functest.tests.unit import test_utils + + +class CliEnvTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.cli_environ = cli_env.CliEnv() + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_testcase.ft_utils.execute_command') + def test_prepare_default(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/prepare_env.py start" % + CONST.dir_repo_functest) + self.cli_environ.prepare() + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_testcase.ft_utils.execute_command') + def test_prepare_missing_status(self, mock_ft_utils, mock_os): + with mock.patch('__builtin__.raw_input', return_value="y"), \ + mock.patch('functest.cli.commands.cli_testcase.os.remove') \ + as mock_os_remove: + cmd = ("python %s/functest/ci/prepare_env.py start" % + CONST.dir_repo_functest) + self.cli_environ.prepare() + mock_os_remove.assert_called_once_with(CONST.env_active) + mock_ft_utils.assert_called_with(cmd) + + def _test_show_missing_env_var(self, var, *args): + if var == 'INSTALLER_TYPE': + CONST.INSTALLER_TYPE = None + reg_string = "| INSTALLER: Unknown, \S+\s*|" + elif var == 'INSTALLER_IP': + CONST.INSTALLER_IP = None + reg_string = "| INSTALLER: \S+, Unknown\s*|" + elif var == 'SCENARIO': + CONST.DEPLOY_SCENARIO = None + reg_string = "| SCENARIO: Unknown\s*|" + elif var == 'NODE': + CONST.NODE_NAME = None + reg_string = "| POD: Unknown\s*|" + elif var == 'BUILD_TAG': + CONST.BUILD_TAG = None + reg_string = "| BUILD TAG: None|" + elif var == 'DEBUG': + CONST.CI_DEBUG = None + reg_string = "| DEBUG FLAG: false\s*|" + elif var == 'STATUS': + reg_string = "| STATUS: not ready\s*|" + + with mock.patch('functest.cli.commands.cli_env.click.echo') \ + as mock_click_echo: + self.cli_environ.show() + mock_click_echo.assert_called_with(test_utils. + RegexMatch(reg_string)) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + def test_show_missing_ci_installer_type(self, *args): + self._test_show_missing_env_var('INSTALLER_TYPE', *args) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + def test_show_missing_ci_installer_ip(self, *args): + self._test_show_missing_env_var('INSTALLER_IP', *args) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + def test_show_missing_ci_scenario(self, *args): + self._test_show_missing_env_var('SCENARIO', *args) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + def test_show_missing_ci_node(self, *args): + self._test_show_missing_env_var('NODE', *args) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + def test_show_missing_ci_build_tag(self, *args): + self._test_show_missing_env_var('BUILD_TAG', *args) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + def test_show_missing_ci_debug(self, *args): + self._test_show_missing_env_var('DEBUG', *args) + + @mock.patch('functest.cli.commands.cli_env.git.Repo') + @mock.patch('functest.cli.commands.cli_env.os.path.isfile', + return_value=False) + def test_show_missing_environment(self, *args): + self._test_show_missing_env_var('STATUS', *args) + + @mock.patch('functest.cli.commands.cli_env.os.path.exists', + return_value=False) + def test_show_missing_git_repo_dir(self, *args): + CONST.dir_repo_functest = None + self.assertRaises(NoSuchPathError, lambda: self.cli_environ.show()) + + @mock.patch('functest.cli.commands.cli_env.click.echo') + @mock.patch('functest.cli.commands.cli_env.os.path.isfile', + return_value=True) + def test_status_environment_present(self, mock_path, mock_click_echo): + self.assertEqual(self.cli_environ.status(), 0) + mock_click_echo.assert_called_with("Functest environment" + " ready to run tests.\n") + + @mock.patch('functest.cli.commands.cli_env.click.echo') + @mock.patch('functest.cli.commands.cli_env.os.path.isfile', + return_value=False) + def test_status_environment_absent(self, mock_path, mock_click_echo): + self.assertEqual(self.cli_environ.status(), 1) + mock_click_echo.assert_called_with("Functest environment" + " is not installed.\n") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/cli/commands/test_cli_os.py b/functest/tests/unit/cli/commands/test_cli_os.py new file mode 100644 index 000000000..f0e58c676 --- /dev/null +++ b/functest/tests/unit/cli/commands/test_cli_os.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python +# +# jose.lausuch@ericsson.com +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +import logging +import unittest +import os + +import mock + +from functest.cli.commands import cli_os +from functest.utils.constants import CONST + + +class CliOpenStackTesting(unittest.TestCase): + logging.disable(logging.CRITICAL) + + def setUp(self): + self.endpoint_ip = 'test_ip' + self.os_auth_url = 'http://test_ip:test_port/v2.0' + self.installer_type = 'test_installer_type' + self.installer_ip = 'test_installer_ip' + self.openstack_creds = 'test_openstack_creds' + self.dir_repo_functest = 'test_dir_repo_functest' + self.snapshot_file = 'test_snapshot_file' + self.cli_os = cli_os.CliOpenStack() + + def test_ping_endpoint_default(self): + self.cli_os.os_auth_url = self.os_auth_url + self.cli_os.endpoint_ip = self.endpoint_ip + with mock.patch('functest.cli.commands.cli_os.os.system', + return_value=0): + self.assertEqual(self.cli_os.ping_endpoint(), 0) + + @mock.patch('functest.cli.commands.cli_os.exit', side_effect=Exception) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_ping_endpoint_missing_auth_url(self, mock_click_echo, + mock_exit): + with self.assertRaises(Exception): + self.cli_os.os_auth_url = None + self.cli_os.ping_endpoint() + mock_click_echo.assert_called_once_with("Source the OpenStack " + "credentials first '. " + "$creds'") + + @mock.patch('functest.cli.commands.cli_os.exit') + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_ping_endpoint_os_system_fails(self, mock_click_echo, + mock_exit): + self.cli_os.os_auth_url = self.os_auth_url + self.cli_os.endpoint_ip = self.endpoint_ip + with mock.patch('functest.cli.commands.cli_os.os.system', + return_value=1): + self.cli_os.ping_endpoint() + mock_click_echo.assert_called_once_with("Cannot talk to the " + "endpoint %s\n" % + self.endpoint_ip) + mock_exit.assert_called_once_with(0) + + @mock.patch('functest.cli.commands.cli_os.ft_utils.execute_command') + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_fetch_credentials_default(self, mock_click_echo, + mock_os_path, + mock_ftutils_execute): + CONST.INSTALLER_TYPE = self.installer_type + CONST.INSTALLER_IP = self.installer_ip + cmd = ("%s/releng/utils/fetch_os_creds.sh -d %s -i %s -a %s" + % (CONST.dir_repos, + self.openstack_creds, + self.installer_type, + self.installer_ip)) + self.cli_os.openstack_creds = self.openstack_creds + self.cli_os.fetch_credentials() + mock_click_echo.assert_called_once_with("Fetching credentials from " + "installer node '%s' with " + "IP=%s.." % + (self.installer_type, + self.installer_ip)) + mock_ftutils_execute.assert_called_once_with(cmd, verbose=False) + + @mock.patch('functest.cli.commands.cli_os.ft_utils.execute_command') + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_fetch_credentials_missing_installer_type(self, mock_click_echo, + mock_os_path, + mock_ftutils_execute): + installer_type = None + installer_ip = self.installer_ip + CONST.INSTALLER_TYPE = installer_type + CONST.INSTALLER_IP = installer_ip + cmd = ("%s/releng/utils/fetch_os_creds.sh -d %s -i %s -a %s" + % (CONST.dir_repos, + self.openstack_creds, + installer_type, + installer_ip)) + self.cli_os.openstack_creds = self.openstack_creds + self.cli_os.fetch_credentials() + mock_click_echo.assert_any_call("The environment variable " + "'INSTALLER_TYPE' is not" + "defined. Please export it") + mock_click_echo.assert_any_call("Fetching credentials from " + "installer node '%s' with " + "IP=%s.." % + (installer_type, + installer_ip)) + mock_ftutils_execute.assert_called_once_with(cmd, verbose=False) + + @mock.patch('functest.cli.commands.cli_os.ft_utils.execute_command') + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_fetch_credentials_missing_installer_ip(self, mock_click_echo, + mock_os_path, + mock_ftutils_execute): + installer_type = self.installer_type + installer_ip = None + CONST.INSTALLER_TYPE = installer_type + CONST.INSTALLER_IP = installer_ip + cmd = ("%s/releng/utils/fetch_os_creds.sh -d %s -i %s -a %s" + % (CONST.dir_repos, + self.openstack_creds, + installer_type, + installer_ip)) + self.cli_os.openstack_creds = self.openstack_creds + self.cli_os.fetch_credentials() + mock_click_echo.assert_any_call("The environment variable " + "'INSTALLER_IP' is not" + "defined. Please export it") + mock_click_echo.assert_any_call("Fetching credentials from " + "installer node '%s' with " + "IP=%s.." % + (installer_type, + installer_ip)) + mock_ftutils_execute.assert_called_once_with(cmd, verbose=False) + + @mock.patch('functest.cli.commands.cli_os.ft_utils.execute_command') + def test_check(self, mock_ftutils_execute): + with mock.patch.object(self.cli_os, 'ping_endpoint'): + CONST.dir_repo_functest = self.dir_repo_functest + cmd = CONST.dir_repo_functest + "/functest/ci/check_os.sh" + self.cli_os.check() + mock_ftutils_execute.assert_called_once_with(cmd, verbose=False) + + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_snapshot_create(self, mock_click_echo, mock_os_path): + with mock.patch.object(self.cli_os, 'ping_endpoint'), \ + mock.patch('functest.cli.commands.cli_os.os_snapshot.main') \ + as mock_os_snapshot: + self.cli_os.snapshot_create() + mock_click_echo.assert_called_once_with("Generating Openstack " + "snapshot...") + self.assertTrue(mock_os_snapshot.called) + + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_snapshot_create_overwrite(self, mock_click_echo, mock_os_path): + with mock.patch('__builtin__.raw_input', return_value="y") \ + as mock_raw_input, \ + mock.patch.object(self.cli_os, 'ping_endpoint'), \ + mock.patch('functest.cli.commands.cli_os.os_snapshot.main') \ + as mock_os_snapshot: + self.cli_os.snapshot_create() + mock_click_echo.assert_called_once_with("Generating Openstack " + "snapshot...") + mock_raw_input.assert_any_call("It seems there is already an " + "OpenStack snapshot. Do you want " + "to overwrite it with the current " + "OpenStack status? [y|n]\n") + self.assertTrue(mock_os_snapshot.called) + + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_snapshot_show_missing_snap(self, mock_click_echo, mock_os_path): + self.cli_os.snapshot_show() + mock_click_echo.assert_called_once_with("There is no OpenStack " + "snapshot created. To create " + "one run the command " + "'functest openstack " + "snapshot-create'") + + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_snapshot_show_default(self, mock_click_echo, mock_os_path): + with mock.patch('__builtin__.open', mock.mock_open(read_data='0')) \ + as m: + self.cli_os.snapshot_file = self.snapshot_file + self.cli_os.snapshot_show() + m.assert_called_once_with(self.snapshot_file, 'r') + mock_click_echo.assert_called_once_with("\n0") + + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_clean(self, mock_click_echo, mock_os_path): + with mock.patch.object(self.cli_os, 'ping_endpoint'), \ + mock.patch('functest.cli.commands.cli_os.os_clean.main') \ + as mock_os_clean: + self.cli_os.clean() + self.assertTrue(mock_os_clean.called) + + @mock.patch('functest.cli.commands.cli_os.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_clean_missing_file(self, mock_click_echo, mock_os_path): + with mock.patch.object(self.cli_os, 'ping_endpoint'): + self.cli_os.clean() + mock_click_echo.assert_called_once_with("Not possible to clean " + "OpenStack without a " + "snapshot. This could " + "cause problems. " + "Run first the command " + "'functest openstack " + "snapshot-create'") + + @mock.patch('functest.cli.commands.cli_os.click.echo') + def test_show_credentials(self, mock_click_echo): + key = 'OS_KEY' + value = 'OS_VALUE' + with mock.patch.dict(os.environ, {key: value}): + self.cli_os.show_credentials() + mock_click_echo.assert_any_call("{}={}".format(key, value)) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/cli/commands/test_cli_testcase.py b/functest/tests/unit/cli/commands/test_cli_testcase.py new file mode 100644 index 000000000..39c8139d7 --- /dev/null +++ b/functest/tests/unit/cli/commands/test_cli_testcase.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + + +import logging +import unittest + +import mock + +from functest.cli.commands import cli_testcase +from functest.utils.constants import CONST + + +class CliTestCasesTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.testname = 'testname' + with mock.patch('functest.cli.commands.cli_testcase.tb'): + self.cli_tests = cli_testcase.CliTestcase() + + @mock.patch('functest.cli.commands.cli_testcase.vacation.main') + def test_run_vacation(self, mock_method): + self.cli_tests.run('vacation') + self.assertTrue(mock_method.called) + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_testcase.click.echo') + def test_run_missing_env_file(self, mock_click_echo, mock_os): + self.cli_tests.run(self.testname) + mock_click_echo.assert_called_with("Functest environment is not ready." + " Run first 'functest env prepare'") + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_testcase.ft_utils.execute_command') + def test_run_default(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "-n -r ", self.testname)) + self.cli_tests.run(self.testname, noclean=True, report=True) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_testcase.ft_utils.execute_command') + def test_run_noclean_missing_report(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "-n ", self.testname)) + self.cli_tests.run(self.testname, noclean=True, report=False) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_testcase.ft_utils.execute_command') + def test_run_report_missing_noclean(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "-r ", self.testname)) + self.cli_tests.run(self.testname, noclean=False, report=True) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_testcase.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_testcase.ft_utils.execute_command') + def test_run_missing_noclean_report(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "", self.testname)) + self.cli_tests.run(self.testname, noclean=False, report=False) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_testcase.click.echo') + def test_list(self, mock_click_echo): + with mock.patch.object(self.cli_tests.tiers, 'get_tiers', + return_value=[]): + self.cli_tests.list() + mock_click_echo.assert_called_with("") + + @mock.patch('functest.cli.commands.cli_testcase.click.echo') + def test_show_default_desc_none(self, mock_click_echo): + with mock.patch.object(self.cli_tests.tiers, 'get_test', + return_value=None): + self.cli_tests.show(self.testname) + mock_click_echo.assert_any_call("The test case '%s' " + "does not exist or is" + " not supported." + % self.testname) + + @mock.patch('functest.cli.commands.cli_testcase.click.echo') + def test_show_default(self, mock_click_echo): + mock_obj = mock.Mock() + with mock.patch.object(self.cli_tests.tiers, 'get_test', + return_value=mock_obj): + self.cli_tests.show(self.testname) + mock_click_echo.assert_called_with(mock_obj) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/cli/commands/test_cli_tier.py b/functest/tests/unit/cli/commands/test_cli_tier.py new file mode 100644 index 000000000..802359f16 --- /dev/null +++ b/functest/tests/unit/cli/commands/test_cli_tier.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python + +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + + +import logging +import unittest + +import mock + +from functest.cli.commands import cli_tier +from functest.utils.constants import CONST + + +class CliTierTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.tiername = 'tiername' + self.testnames = 'testnames' + with mock.patch('functest.cli.commands.cli_tier.tb'): + self.cli_tier = cli_tier.CliTier() + + @mock.patch('functest.cli.commands.cli_tier.click.echo') + def test_list(self, mock_click_echo): + with mock.patch.object(self.cli_tier.tiers, 'get_tiers', + return_value=[]): + self.cli_tier.list() + mock_click_echo.assert_called_with("") + + @mock.patch('functest.cli.commands.cli_tier.click.echo') + def test_show_default(self, mock_click_echo): + with mock.patch.object(self.cli_tier.tiers, 'get_tier', + return_value=self.tiername): + self.cli_tier.show(self.tiername) + mock_click_echo.assert_called_with(self.tiername) + + @mock.patch('functest.cli.commands.cli_tier.click.echo') + def test_show_missing_tier(self, mock_click_echo): + with mock.patch.object(self.cli_tier.tiers, 'get_tier', + return_value=None), \ + mock.patch.object(self.cli_tier.tiers, 'get_tier_names', + return_value='tiernames'): + self.cli_tier.show(self.tiername) + mock_click_echo.assert_called_with("The tier with name '%s' does " + "not exist. Available tiers are" + ":\n %s\n" % (self.tiername, + 'tiernames')) + + @mock.patch('functest.cli.commands.cli_tier.click.echo') + def test_gettests_default(self, mock_click_echo): + mock_obj = mock.Mock() + attrs = {'get_test_names.return_value': self.testnames} + mock_obj.configure_mock(**attrs) + + with mock.patch.object(self.cli_tier.tiers, 'get_tier', + return_value=mock_obj): + self.cli_tier.gettests(self.tiername) + mock_click_echo.assert_called_with("Test cases in tier " + "'%s':\n %s\n" % (self.tiername, + self.testnames + )) + + @mock.patch('functest.cli.commands.cli_tier.click.echo') + def test_gettests_missing_tier(self, mock_click_echo): + with mock.patch.object(self.cli_tier.tiers, 'get_tier', + return_value=None), \ + mock.patch.object(self.cli_tier.tiers, 'get_tier_names', + return_value='tiernames'): + self.cli_tier.gettests(self.tiername) + mock_click_echo.assert_called_with("The tier with name '%s' does " + "not exist. Available tiers are" + ":\n %s\n" % (self.tiername, + 'tiernames')) + + @mock.patch('functest.cli.commands.cli_tier.os.path.isfile', + return_value=False) + @mock.patch('functest.cli.commands.cli_tier.click.echo') + def test_run_missing_env_file(self, mock_click_echo, mock_os): + self.cli_tier.run(self.tiername) + mock_click_echo.assert_called_with("Functest environment is not ready." + " Run first 'functest env prepare'") + + @mock.patch('functest.cli.commands.cli_tier.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_tier.ft_utils.execute_command') + def test_run_default(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "-n -r ", + self.tiername)) + self.cli_tier.run(self.tiername, noclean=True, report=True) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_tier.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_tier.ft_utils.execute_command') + def test_run_report_missing_noclean(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "-r ", + self.tiername)) + self.cli_tier.run(self.tiername, noclean=False, report=True) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_tier.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_tier.ft_utils.execute_command') + def test_run_noclean_missing_report(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "-n ", + self.tiername)) + self.cli_tier.run(self.tiername, noclean=True, report=False) + mock_ft_utils.assert_called_with(cmd) + + @mock.patch('functest.cli.commands.cli_tier.os.path.isfile', + return_value=True) + @mock.patch('functest.cli.commands.cli_tier.ft_utils.execute_command') + def test_run_missing_noclean_report(self, mock_ft_utils, mock_os): + cmd = ("python %s/functest/ci/run_tests.py " + "%s -t %s" % (CONST.dir_repo_functest, "", + self.tiername)) + self.cli_tier.run(self.tiername, noclean=False, report=False) + mock_ft_utils.assert_called_with(cmd) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/cli/test_cli_base.py b/functest/tests/unit/cli/test_cli_base.py new file mode 100644 index 000000000..fe065c2aa --- /dev/null +++ b/functest/tests/unit/cli/test_cli_base.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +# Copyright (c) 2016 Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import logging +import unittest + +import mock +from click.testing import CliRunner + +with mock.patch('functest.cli.commands.cli_testcase.CliTestcase.__init__', + mock.Mock(return_value=None)), \ + mock.patch('functest.cli.commands.cli_tier.CliTier.__init__', + mock.Mock(return_value=None)): + from functest.cli import cli_base + + +class CliBaseTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.runner = CliRunner() + self._openstack = cli_base._openstack + self._env = cli_base._env + self._testcase = cli_base._testcase + self._tier = cli_base._tier + + def test_os_check(self): + with mock.patch.object(self._openstack, 'check') as mock_method: + result = self.runner.invoke(cli_base.os_check) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_os_snapshot_create(self): + with mock.patch.object(self._openstack, 'snapshot_create') \ + as mock_method: + result = self.runner.invoke(cli_base.os_snapshot_create) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_os_snapshot_show(self): + with mock.patch.object(self._openstack, 'snapshot_show') \ + as mock_method: + result = self.runner.invoke(cli_base.os_snapshot_show) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_os_clean(self): + with mock.patch.object(self._openstack, 'clean') as mock_method: + result = self.runner.invoke(cli_base.os_clean) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_os_show_credentials(self): + with mock.patch.object(self._openstack, 'show_credentials') \ + as mock_method: + result = self.runner.invoke(cli_base.os_show_credentials) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_os_fetch_rc(self): + with mock.patch.object(self._openstack, 'fetch_credentials') \ + as mock_method: + result = self.runner.invoke(cli_base.os_fetch_rc) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_env_prepare(self): + with mock.patch.object(self._env, 'prepare') as mock_method: + result = self.runner.invoke(cli_base.env_prepare) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_env_show(self): + with mock.patch.object(self._env, 'show') as mock_method: + result = self.runner.invoke(cli_base.env_show) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_env_status(self): + with mock.patch.object(self._env, 'status') as mock_method: + result = self.runner.invoke(cli_base.env_status) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_testcase_list(self): + with mock.patch.object(self._testcase, 'list') as mock_method: + result = self.runner.invoke(cli_base.testcase_list) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_testcase_show(self): + with mock.patch.object(self._testcase, 'show') as mock_method: + result = self.runner.invoke(cli_base.testcase_show, ['testname']) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_testcase_run(self): + with mock.patch.object(self._testcase, 'run') as mock_method: + result = self.runner.invoke(cli_base.testcase_run, + ['testname', '--noclean']) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_tier_list(self): + with mock.patch.object(self._tier, 'list') as mock_method: + result = self.runner.invoke(cli_base.tier_list) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_tier_show(self): + with mock.patch.object(self._tier, 'show') as mock_method: + result = self.runner.invoke(cli_base.tier_show, ['tiername']) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_tier_gettests(self): + with mock.patch.object(self._tier, 'gettests') as mock_method: + result = self.runner.invoke(cli_base.tier_gettests, ['tiername']) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + def test_tier_run(self): + with mock.patch.object(self._tier, 'run') as mock_method: + result = self.runner.invoke(cli_base.tier_run, + ['tiername', '--noclean']) + self.assertEqual(result.exit_code, 0) + self.assertTrue(mock_method.called) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/core/test_testcase_base.py b/functest/tests/unit/core/test_testcase_base.py index fe7b0d054..b6efa40dc 100644 --- a/functest/tests/unit/core/test_testcase_base.py +++ b/functest/tests/unit/core/test_testcase_base.py @@ -11,7 +11,8 @@ import logging import mock import unittest -import functest.core.testcase_base as testcase_base +mock.patch('logging.FileHandler').start() # noqa +from functest.core import testcase_base class TestcaseBaseTesting(unittest.TestCase): @@ -24,7 +25,7 @@ class TestcaseBaseTesting(unittest.TestCase): self.test.case_name = "base" self.test.start_time = "1" self.test.stop_time = "2" - self.test.criteria = "100" + self.test.criteria = "PASS" self.test.details = {"Hello": "World"} def test_run_unimplemented(self): @@ -82,6 +83,21 @@ class TestcaseBaseTesting(unittest.TestCase): self.test.project, self.test.case_name, self.test.start_time, self.test.stop_time, self.test.criteria, self.test.details) + def test_check_criteria_missing(self): + self.test.criteria = None + self.assertEqual(self.test.check_criteria(), + testcase_base.TestcaseBase.EX_TESTCASE_FAILED) + + def test_check_criteria_failed(self): + self.test.criteria = 'FAILED' + self.assertEqual(self.test.check_criteria(), + testcase_base.TestcaseBase.EX_TESTCASE_FAILED) + + def test_check_criteria_pass(self): + self.test.criteria = 'PASS' + self.assertEqual(self.test.check_criteria(), + testcase_base.TestcaseBase.EX_OK) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/functest/tests/unit/odl/test_odl.py b/functest/tests/unit/odl/test_odl.py index ef18016bf..d45d5628e 100644 --- a/functest/tests/unit/odl/test_odl.py +++ b/functest/tests/unit/odl/test_odl.py @@ -11,13 +11,17 @@ import errno import logging import mock import os +import StringIO import unittest -from robot.errors import RobotError +from keystoneauth1.exceptions import auth_plugins +from robot.errors import DataError, RobotError +from robot.result import testcase +from robot.utils.robottime import timestamp_to_secs -import functest.core.testcase_base as testcase_base +mock.patch('logging.FileHandler').start() # noqa +from functest.core import testcase_base from functest.opnfv_tests.sdn.odl import odl -from functest.utils import functest_constants as ft_constants class ODLTesting(unittest.TestCase): @@ -36,19 +40,111 @@ class ODLTesting(unittest.TestCase): _odl_password = "admin" def setUp(self): - ft_constants.OS_USERNAME = self._os_username - ft_constants.OS_PASSWORD = self._os_password - ft_constants.OS_TENANT_NAME = self._os_tenantname + for var in ("INSTALLER_TYPE", "SDN_CONTROLLER", "SDN_CONTROLLER_IP"): + if var in os.environ: + del os.environ[var] + os.environ["OS_USERNAME"] = self._os_username + os.environ["OS_PASSWORD"] = self._os_password + os.environ["OS_TENANT_NAME"] = self._os_tenantname self.test = odl.ODLTests() + self.defaultargs = {'odlusername': self._odl_username, + 'odlpassword': self._odl_password, + 'keystoneip': self._keystone_ip, + 'neutronip': self._keystone_ip, + 'osusername': self._os_username, + 'ostenantname': self._os_tenantname, + 'ospassword': self._os_password, + 'odlip': self._keystone_ip, + 'odlwebport': self._odl_webport, + 'odlrestconfport': self._odl_restconfport, + 'pushtodb': False} + + def test_empty_visitor(self): + visitor = odl.ODLResultVisitor() + self.assertFalse(visitor.get_data()) + + def test_visitor(self): + visitor = odl.ODLResultVisitor() + data = {'name': 'foo', + 'parent': 'bar', + 'status': 'PASS', + 'starttime': "20161216 16:00:00.000", + 'endtime': "20161216 16:00:01.000", + 'elapsedtime': 1000, + 'text': 'Hello, World!', + 'critical': True} + test = testcase.TestCase(name=data['name'], + status=data['status'], + message=data['text'], + starttime=data['starttime'], + endtime=data['endtime']) + test.parent = mock.Mock() + config = {'name': data['parent'], + 'criticality.test_is_critical.return_value': data[ + 'critical']} + test.parent.configure_mock(**config) + visitor.visit_test(test) + self.assertEqual(visitor.get_data(), [data]) + + @mock.patch('robot.api.ExecutionResult', side_effect=DataError) + def test_parse_results_raises_exceptions(self, *args): + with self.assertRaises(DataError): + self.test.parse_results() + + def test_parse_results(self, *args): + config = {'name': 'dummy', 'starttime': '20161216 16:00:00.000', + 'endtime': '20161216 16:00:01.000', 'status': 'PASS'} + suite = mock.Mock() + suite.configure_mock(**config) + with mock.patch('robot.api.ExecutionResult', + return_value=mock.Mock(suite=suite)): + self.test.parse_results() + self.assertEqual(self.test.criteria, config['status']) + self.assertEqual(self.test.start_time, + timestamp_to_secs(config['starttime'])) + self.assertEqual(self.test.stop_time, + timestamp_to_secs(config['endtime'])) + self.assertEqual(self.test.details, + {'description': config['name'], 'tests': []}) @mock.patch('fileinput.input', side_effect=Exception()) def test_set_robotframework_vars_failed(self, *args): self.assertFalse(self.test.set_robotframework_vars()) @mock.patch('fileinput.input', return_value=[]) - def test_set_robotframework_vars(self, args): + def test_set_robotframework_vars_empty(self, args): self.assertTrue(self.test.set_robotframework_vars()) + @mock.patch('sys.stdout', new_callable=StringIO.StringIO) + def _test_set_robotframework_vars(self, msg1, msg2, *args): + line = mock.MagicMock() + line.__iter__.return_value = [msg1] + with mock.patch('fileinput.input', return_value=line) as mock_method: + self.assertTrue(self.test.set_robotframework_vars()) + mock_method.assert_called_once_with( + os.path.join(odl.ODLTests.odl_test_repo, + 'csit/variables/Variables.py'), inplace=True) + self.assertEqual(args[0].getvalue(), "{}\n".format(msg2)) + + def test_set_robotframework_vars_auth_default(self): + self._test_set_robotframework_vars("AUTH = []", + "AUTH = [u'admin', u'admin']") + + def test_set_robotframework_vars_auth1(self): + self._test_set_robotframework_vars("AUTH1 = []", "AUTH1 = []") + + @mock.patch('sys.stdout', new_callable=StringIO.StringIO) + def test_set_robotframework_vars_auth_foo(self, *args): + line = mock.MagicMock() + line.__iter__.return_value = ["AUTH = []"] + with mock.patch('fileinput.input', return_value=line) as mock_method: + self.assertTrue(self.test.set_robotframework_vars('foo', 'bar')) + mock_method.assert_called_once_with( + os.path.join(odl.ODLTests.odl_test_repo, + 'csit/variables/Variables.py'), inplace=True) + self.assertEqual(args[0].getvalue(), + "AUTH = [u'{}', u'{}']\n".format('foo', 'bar')) + @classmethod def _fake_url_for(cls, service_type='identity', **kwargs): if service_type == 'identity': @@ -59,14 +155,6 @@ class ODLTesting(unittest.TestCase): else: return None - @classmethod - def _get_fake_keystone_client(cls): - kclient = mock.Mock() - kclient.service_catalog = mock.Mock() - kclient.service_catalog.url_for = mock.Mock( - side_effect=cls._fake_url_for) - return kclient - def _get_main_kwargs(self, key=None): kwargs = {'odlusername': self._odl_username, 'odlpassword': self._odl_password, @@ -85,9 +173,9 @@ class ODLTesting(unittest.TestCase): def _test_main(self, status, *args): kwargs = self._get_main_kwargs() self.assertEqual(self.test.main(**kwargs), status) - odl_res_dir = odl.ODLTests.res_dir if len(args) > 0: - args[0].assert_called_once_with(odl_res_dir) + args[0].assert_called_once_with( + odl.ODLTests.res_dir) if len(args) > 1: variable = ['KEYSTONE:{}'.format(self._keystone_ip), 'NEUTRON:{}'.format(self._neutron_ip), @@ -97,18 +185,17 @@ class ODLTesting(unittest.TestCase): 'ODL_SYSTEM_IP:{}'.format(self._sdn_controller_ip), 'PORT:{}'.format(self._odl_webport), 'RESTCONFPORT:{}'.format(self._odl_restconfport)] - output_file = os.path.join(odl_res_dir, 'output.xml') args[1].assert_called_once_with( odl.ODLTests.basic_suite_dir, odl.ODLTests.neutron_suite_dir, log='NONE', - output=output_file, + output=os.path.join(odl.ODLTests.res_dir, 'output.xml'), report='NONE', stdout=mock.ANY, variable=variable) if len(args) > 2: - stdout_file = os.path.join(odl_res_dir, 'stdout.txt') - args[2].assert_called_with(stdout_file) + args[2].assert_called_with( + os.path.join(odl.ODLTests.res_dir, 'stdout.txt')) def _test_main_missing_keyword(self, key): kwargs = self._get_main_kwargs(key) @@ -172,6 +259,8 @@ class ODLTesting(unittest.TestCase): def test_main_robot_run_failed(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ + mock.patch.object(odl, 'open', mock.mock_open(), + create=True), \ self.assertRaises(RobotError): self._test_main(testcase_base.TestcaseBase.EX_RUN_ERROR, *args) @@ -180,6 +269,8 @@ class ODLTesting(unittest.TestCase): def test_main_parse_results_failed(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ + mock.patch.object(odl, 'open', mock.mock_open(), + create=True), \ mock.patch.object(self.test, 'parse_results', side_effect=RobotError): self._test_main(testcase_base.TestcaseBase.EX_RUN_ERROR, *args) @@ -200,8 +291,9 @@ class ODLTesting(unittest.TestCase): def test_main(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(odl, 'open', mock.mock_open(), + create=True), \ + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) @mock.patch('os.remove') @@ -210,8 +302,9 @@ class ODLTesting(unittest.TestCase): def test_main_makedirs_oserror17(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(odl, 'open', mock.mock_open(), + create=True), \ + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) @mock.patch('os.remove') @@ -220,8 +313,9 @@ class ODLTesting(unittest.TestCase): def test_main_testcases_in_failure(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(odl, 'open', mock.mock_open(), + create=True), \ + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) @mock.patch('os.remove', side_effect=OSError) @@ -230,25 +324,22 @@ class ODLTesting(unittest.TestCase): def test_main_remove_oserror(self, *args): with mock.patch.object(self.test, 'set_robotframework_vars', return_value=True), \ - mock.patch.object(self.test, 'parse_results', - return_value="PASS"): + mock.patch.object(odl, 'open', mock.mock_open(), + create=True), \ + mock.patch.object(self.test, 'parse_results'): self._test_main(testcase_base.TestcaseBase.EX_OK, *args) def _test_run_missing_env_var(self, var): - if var == 'OS_USERNAME': - ft_constants.OS_USERNAME = None - elif var == 'OS_PASSWORD': - ft_constants.OS_PASSWORD = None - elif var == 'OS_TENANT_NAME': - ft_constants.OS_TENANT_NAME = None - - self.assertEqual(self.test.run(), - testcase_base.TestcaseBase.EX_RUN_ERROR) + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): + del os.environ[var] + self.assertEqual(self.test.run(), + testcase_base.TestcaseBase.EX_RUN_ERROR) def _test_run(self, status=testcase_base.TestcaseBase.EX_OK, exception=None, odlip="127.0.0.3", odlwebport="8080"): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): if exception: self.test.main = mock.Mock(side_effect=exception) else: @@ -262,6 +353,12 @@ class ODLTesting(unittest.TestCase): ospassword=self._os_password, ostenantname=self._os_tenantname, osusername=self._os_username) + def test_run_exception(self): + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=auth_plugins.MissingAuthPlugin()): + self.assertEqual(self.test.run(), + testcase_base.TestcaseBase.EX_RUN_ERROR) + def test_run_missing_os_username(self): self._test_run_missing_env_var("OS_USERNAME") @@ -272,75 +369,138 @@ class ODLTesting(unittest.TestCase): self._test_run_missing_env_var("OS_TENANT_NAME") def test_run_main_false(self): - ft_constants.CI_INSTALLER_TYPE = None - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip self._test_run(testcase_base.TestcaseBase.EX_RUN_ERROR, odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) def test_run_main_exception(self): - ft_constants.CI_INSTALLER_TYPE = None - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip with self.assertRaises(Exception): + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip self._test_run(status=testcase_base.TestcaseBase.EX_RUN_ERROR, exception=Exception(), odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) def test_run_missing_sdn_controller_ip(self): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): - ft_constants.CI_INSTALLER_TYPE = None - ft_constants.SDN_CONTROLLER_IP = None + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) def test_run_without_installer_type(self): - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip - ft_constants.CI_INSTALLER_TYPE = None + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._sdn_controller_ip, odlwebport=self._odl_webport) def test_run_fuel(self): - ft_constants.CI_INSTALLER_TYPE = "fuel" + os.environ["INSTALLER_TYPE"] = "fuel" self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._neutron_ip, odlwebport='8282') def test_run_apex_missing_sdn_controller_ip(self): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): - ft_constants.CI_INSTALLER_TYPE = "apex" - ft_constants.SDN_CONTROLLER_IP = None + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): + os.environ["INSTALLER_TYPE"] = "apex" self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) def test_run_apex(self): - ft_constants.SDN_CONTROLLER_IP = self._sdn_controller_ip - ft_constants.CI_INSTALLER_TYPE = "apex" + os.environ["SDN_CONTROLLER_IP"] = self._sdn_controller_ip + os.environ["INSTALLER_TYPE"] = "apex" self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._sdn_controller_ip, odlwebport='8181') def test_run_joid_missing_sdn_controller(self): - with mock.patch('functest.utils.openstack_utils.get_keystone_client', - return_value=self._get_fake_keystone_client()): - ft_constants.CI_INSTALLER_TYPE = "joid" - ft_constants.SDN_CONTROLLER = None + with mock.patch('functest.utils.openstack_utils.get_endpoint', + side_effect=self._fake_url_for): + os.environ["INSTALLER_TYPE"] = "joid" self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) def test_run_joid(self): - ft_constants.SDN_CONTROLLER = self._sdn_controller_ip - ft_constants.CI_INSTALLER_TYPE = "joid" + os.environ["SDN_CONTROLLER"] = self._sdn_controller_ip + os.environ["INSTALLER_TYPE"] = "joid" self._test_run(testcase_base.TestcaseBase.EX_OK, - odlip=self._sdn_controller_ip, - odlwebport=self._odl_webport) + odlip=self._sdn_controller_ip, odlwebport='8080') def test_run_compass(self, *args): - ft_constants.CI_INSTALLER_TYPE = "compass" + os.environ["INSTALLER_TYPE"] = "compass" self._test_run(testcase_base.TestcaseBase.EX_OK, odlip=self._neutron_ip, odlwebport='8181') + def test_argparser_default(self): + parser = odl.ODLParser() + self.assertEqual(parser.parse_args(), self.defaultargs) + + def test_argparser_basic(self): + self.defaultargs['neutronip'] = self._neutron_ip + self.defaultargs['odlip'] = self._sdn_controller_ip + parser = odl.ODLParser() + self.assertEqual(parser.parse_args( + ["--neutronip={}".format(self._neutron_ip), + "--odlip={}".format(self._sdn_controller_ip) + ]), self.defaultargs) + + @mock.patch('sys.stderr', new_callable=StringIO.StringIO) + def test_argparser_fail(self, *args): + self.defaultargs['foo'] = 'bar' + parser = odl.ODLParser() + with self.assertRaises(SystemExit): + parser.parse_args(["--foo=bar"]) + + def _test_argparser(self, arg, value): + self.defaultargs[arg] = value + parser = odl.ODLParser() + self.assertEqual(parser.parse_args(["--{}={}".format(arg, value)]), + self.defaultargs) + + def test_argparser_odlusername(self): + self._test_argparser('odlusername', 'foo') + + def test_argparser_odlpassword(self): + self._test_argparser('odlpassword', 'foo') + + def test_argparser_keystoneip(self): + self._test_argparser('keystoneip', '127.0.0.4') + + def test_argparser_neutronip(self): + self._test_argparser('neutronip', '127.0.0.4') + + def test_argparser_osusername(self): + self._test_argparser('osusername', 'foo') + + def test_argparser_ostenantname(self): + self._test_argparser('ostenantname', 'foo') + + def test_argparser_ospassword(self): + self._test_argparser('ospassword', 'foo') + + def test_argparser_odlip(self): + self._test_argparser('odlip', '127.0.0.4') + + def test_argparser_odlwebport(self): + self._test_argparser('odlwebport', '80') + + def test_argparser_odlrestconfport(self): + self._test_argparser('odlrestconfport', '80') + + def test_argparser_pushtodb(self): + self.defaultargs['pushtodb'] = True + parser = odl.ODLParser() + self.assertEqual(parser.parse_args(["--{}".format('pushtodb')]), + self.defaultargs) + + def test_argparser_multiple_args(self): + self.defaultargs['neutronip'] = self._neutron_ip + self.defaultargs['odlip'] = self._sdn_controller_ip + parser = odl.ODLParser() + self.assertEqual(parser.parse_args( + ["--neutronip={}".format(self._neutron_ip), + "--odlip={}".format(self._sdn_controller_ip) + ]), self.defaultargs) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/functest/tests/unit/test_utils.py b/functest/tests/unit/test_utils.py new file mode 100644 index 000000000..e171db022 --- /dev/null +++ b/functest/tests/unit/test_utils.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import re + + +class RegexMatch(str): + def __eq__(self, other): + match = re.search(self, other) + if match: + return True + return False + + +class SubstrMatch(str): + def __eq__(self, other): + if self in other: + return True + return False diff --git a/functest/tests/unit/utils/test_functest_utils.py b/functest/tests/unit/utils/test_functest_utils.py new file mode 100644 index 000000000..c4b566608 --- /dev/null +++ b/functest/tests/unit/utils/test_functest_utils.py @@ -0,0 +1,600 @@ +#!/usr/bin/env python + +# Copyright (c) 2016 Orange and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import logging +import os +import time +import unittest +import urllib2 + +from git.exc import NoSuchPathError +import mock +import requests + +from functest.tests.unit import test_utils +mock.patch('logging.FileHandler').start() # noqa +from functest.utils import functest_utils + + +class FunctestUtilsTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.url = 'http://www.opnfv.org/' + self.timeout = 5 + self.dest_path = 'test_path' + self.repo_path = 'test_repo_path' + self.installer = 'test_installer' + self.scenario = 'test_scenario' + self.build_tag = 'jenkins-functest-fuel-opnfv-jump-2-daily-master-190' + self.version = 'master' + self.node_name = 'test_node_name' + self.project = 'test_project' + self.case_name = 'test_case_name' + self.status = 'test_status' + self.details = 'test_details' + self.db_url = 'test_db_url' + self.success_rate = 2.0 + self.criteria = 'test_criteria==2.0' + self.start_date = 1482624000 + self.stop_date = 1482624000 + self.start_time = time.time() + self.stop_time = time.time() + self.readline = -1 + self.test_ip = ['10.1.23.4', '10.1.14.15', '10.1.16.15'] + self.test_file = 'test_file' + self.error_msg = 'test_error_msg' + self.cmd = 'test_cmd' + self.output_file = 'test_output_file' + self.testname = 'testname' + self.testcase_dict = {'name': 'testname', 'criteria': self.criteria} + self.parameter = 'general.openstack.image_name' + self.config_yaml = 'test_config_yaml-' + self.file_yaml = {'general': {'openstack': {'image_name': + 'test_image_name'}}} + + @mock.patch('urllib2.urlopen', + side_effect=urllib2.URLError('no host given')) + def test_check_internet_connectivity_failed(self, mock_method): + self.assertFalse(functest_utils.check_internet_connectivity()) + mock_method.assert_called_once_with(self.url, timeout=self.timeout) + + @mock.patch('urllib2.urlopen') + def test_check_internet_connectivity_default(self, mock_method): + self.assertTrue(functest_utils.check_internet_connectivity()) + mock_method.assert_called_once_with(self.url, timeout=self.timeout) + + @mock.patch('urllib2.urlopen') + def test_check_internet_connectivity_debian(self, mock_method): + self.url = "https://www.debian.org/" + self.assertTrue(functest_utils.check_internet_connectivity(self.url)) + mock_method.assert_called_once_with(self.url, timeout=self.timeout) + + @mock.patch('urllib2.urlopen', + side_effect=urllib2.URLError('no host given')) + def test_download_url_failed(self, mock_url): + self.assertFalse(functest_utils.download_url(self.url, self.dest_path)) + + @mock.patch('urllib2.urlopen') + def test_download_url_default(self, mock_url): + with mock.patch("__builtin__.open", mock.mock_open()) as m, \ + mock.patch('functest.utils.functest_utils.shutil.copyfileobj')\ + as mock_sh: + name = self.url.rsplit('/')[-1] + dest = self.dest_path + "/" + name + self.assertTrue(functest_utils.download_url(self.url, + self.dest_path)) + m.assert_called_once_with(dest, 'wb') + self.assertTrue(mock_sh.called) + + def test_get_git_branch(self): + with mock.patch('functest.utils.functest_utils.Repo') as mock_repo: + mock_obj2 = mock.Mock() + attrs = {'name': 'test_branch'} + mock_obj2.configure_mock(**attrs) + + mock_obj = mock.Mock() + attrs = {'active_branch': mock_obj2} + mock_obj.configure_mock(**attrs) + + mock_repo.return_value = mock_obj + self.assertEqual(functest_utils.get_git_branch(self.repo_path), + 'test_branch') + + @mock.patch('functest.utils.functest_utils.Repo', + side_effect=NoSuchPathError) + def test_get_git_branch_failed(self, mock_repo): + self.assertRaises(NoSuchPathError, + lambda: functest_utils.get_git_branch(self.repo_path + )) + + @mock.patch('functest.utils.functest_utils.logger.error') + def test_get_installer_type_failed(self, mock_logger_error): + with mock.patch.dict(os.environ, + {}, + clear=True): + self.assertEqual(functest_utils.get_installer_type(), + "Unknown_installer") + mock_logger_error.assert_called_once_with("Impossible to retrieve" + " the installer type") + + def test_get_installer_type_default(self): + with mock.patch.dict(os.environ, + {'INSTALLER_TYPE': 'test_installer'}, + clear=True): + self.assertEqual(functest_utils.get_installer_type(), + self.installer) + + @mock.patch('functest.utils.functest_utils.logger.error') + def test_get_scenario_failed(self, mock_logger_error): + with mock.patch.dict(os.environ, + {}, + clear=True): + self.assertEqual(functest_utils.get_scenario(), + "Unknown_scenario") + mock_logger_error.assert_called_once_with("Impossible to retrieve" + " the scenario") + + def test_get_scenario_default(self): + with mock.patch.dict(os.environ, + {'DEPLOY_SCENARIO': 'test_scenario'}, + clear=True): + self.assertEqual(functest_utils.get_scenario(), + self.scenario) + + @mock.patch('functest.utils.functest_utils.get_build_tag') + def test_get_version_default(self, mock_get_build_tag): + mock_get_build_tag.return_value = self.build_tag + self.assertEqual(functest_utils.get_version(), self.version) + + @mock.patch('functest.utils.functest_utils.get_build_tag') + def test_get_version_unknown(self, mock_get_build_tag): + mock_get_build_tag.return_value = "unknown_build_tag" + self.assertEqual(functest_utils.get_version(), "unknown") + + @mock.patch('functest.utils.functest_utils.logger.error') + def test_get_pod_name_failed(self, mock_logger_error): + with mock.patch.dict(os.environ, + {}, + clear=True): + self.assertEqual(functest_utils.get_pod_name(), + "unknown-pod") + mock_logger_error.assert_called_once_with("Unable to retrieve " + "the POD name from " + "environment. Using " + "pod name 'unknown-pod'") + + def test_get_pod_name_default(self): + with mock.patch.dict(os.environ, + {'NODE_NAME': 'test_node_name'}, + clear=True): + self.assertEqual(functest_utils.get_pod_name(), + self.node_name) + + @mock.patch('functest.utils.functest_utils.logger.error') + def test_get_build_tag_failed(self, mock_logger_error): + with mock.patch.dict(os.environ, + {}, + clear=True): + self.assertEqual(functest_utils.get_build_tag(), + "unknown_build_tag") + mock_logger_error.assert_called_once_with("Impossible to retrieve" + " the build tag") + + def test_get_build_tag_default(self): + with mock.patch.dict(os.environ, + {'BUILD_TAG': self.build_tag}, + clear=True): + self.assertEqual(functest_utils.get_build_tag(), + self.build_tag) + + @mock.patch('functest.utils.functest_utils.get_functest_config') + def test_get_db_url(self, mock_get_functest_config): + mock_get_functest_config.return_value = self.db_url + self.assertEqual(functest_utils.get_db_url(), self.db_url) + mock_get_functest_config.assert_called_once_with('results.test_db_url') + + @mock.patch('functest.utils.functest_utils.logger.info') + def test_logger_test_results(self, mock_logger_info): + with mock.patch('functest.utils.functest_utils.get_pod_name', + return_value=self.node_name), \ + mock.patch('functest.utils.functest_utils.get_scenario', + return_value=self.scenario), \ + mock.patch('functest.utils.functest_utils.get_version', + return_value=self.version), \ + mock.patch('functest.utils.functest_utils.get_build_tag', + return_value=self.build_tag), \ + mock.patch('functest.utils.functest_utils.get_db_url', + return_value=self.db_url): + functest_utils.logger_test_results(self.project, self.case_name, + self.status, self.details) + mock_logger_info.assert_called_once_with( + "\n" + "****************************************\n" + "\t %(p)s/%(n)s results \n\n" + "****************************************\n" + "DB:\t%(db)s\n" + "pod:\t%(pod)s\n" + "version:\t%(v)s\n" + "scenario:\t%(s)s\n" + "status:\t%(c)s\n" + "build tag:\t%(b)s\n" + "details:\t%(d)s\n" + % {'p': self.project, + 'n': self.case_name, + 'db': self.db_url, + 'pod': self.node_name, + 'v': self.version, + 's': self.scenario, + 'c': self.status, + 'b': self.build_tag, + 'd': self.details}) + + def _get_env_dict(self, var): + dic = {'INSTALLER_TYPE': self.installer, + 'DEPLOY_SCENARIO': self.scenario, + 'NODE_NAME': self.node_name, + 'BUILD_TAG': self.build_tag} + dic.pop(var, None) + return dic + + def _test_push_results_to_db_missing_env(self, env_var): + dic = self._get_env_dict(env_var) + with mock.patch('functest.utils.functest_utils.get_db_url', + return_value=self.db_url), \ + mock.patch.dict(os.environ, + dic, + clear=True), \ + mock.patch('functest.utils.functest_utils.logger.error') \ + as mock_logger_error: + functest_utils.push_results_to_db(self.project, self.case_name, + self.start_date, self.stop_date, + self.criteria, self.details) + mock_logger_error.assert_called_once_with("Please set env var: " + + str("\'" + env_var + + "\'")) + + def test_push_results_to_db_missing_installer(self): + self._test_push_results_to_db_missing_env('INSTALLER_TYPE') + + def test_push_results_to_db_missing_scenario(self): + self._test_push_results_to_db_missing_env('DEPLOY_SCENARIO') + + def test_push_results_to_db_missing_nodename(self): + self._test_push_results_to_db_missing_env('NODE_NAME') + + def test_push_results_to_db_missing_buildtag(self): + self._test_push_results_to_db_missing_env('BUILD_TAG') + + def test_push_results_to_db_incorrect_buildtag(self): + dic = self._get_env_dict(None) + dic['BUILD_TAG'] = 'incorrect_build_tag' + with mock.patch('functest.utils.functest_utils.get_db_url', + return_value=self.db_url), \ + mock.patch.dict(os.environ, + dic, + clear=True), \ + mock.patch('functest.utils.functest_utils.logger.error') \ + as mock_logger_error: + self.assertFalse(functest_utils. + push_results_to_db(self.project, self.case_name, + self.start_date, + self.stop_date, + self.criteria, self.details)) + mock_logger_error.assert_called_once_with("Please fix BUILD_TAG" + " env var: incorrect_" + "build_tag") + + def test_push_results_to_db_request_post_failed(self): + dic = self._get_env_dict(None) + with mock.patch('functest.utils.functest_utils.get_db_url', + return_value=self.db_url), \ + mock.patch.dict(os.environ, + dic, + clear=True), \ + mock.patch('functest.utils.functest_utils.logger.error') \ + as mock_logger_error, \ + mock.patch('functest.utils.functest_utils.requests.post', + side_effect=requests.RequestException): + self.assertFalse(functest_utils. + push_results_to_db(self.project, self.case_name, + self.start_date, + self.stop_date, + self.criteria, self.details)) + mock_logger_error.assert_called_once_with(test_utils. + RegexMatch("Pushing " + "Result to" + " DB" + "(\S+\s*) " + "failed:")) + + def test_push_results_to_db_request_post_exception(self): + dic = self._get_env_dict(None) + with mock.patch('functest.utils.functest_utils.get_db_url', + return_value=self.db_url), \ + mock.patch.dict(os.environ, + dic, + clear=True), \ + mock.patch('functest.utils.functest_utils.logger.error') \ + as mock_logger_error, \ + mock.patch('functest.utils.functest_utils.requests.post', + side_effect=Exception): + self.assertFalse(functest_utils. + push_results_to_db(self.project, self.case_name, + self.start_date, + self.stop_date, + self.criteria, self.details)) + self.assertTrue(mock_logger_error.called) + + def test_push_results_to_db_default(self): + dic = self._get_env_dict(None) + with mock.patch('functest.utils.functest_utils.get_db_url', + return_value=self.db_url), \ + mock.patch.dict(os.environ, + dic, + clear=True), \ + mock.patch('functest.utils.functest_utils.requests.post'): + self.assertTrue(functest_utils. + push_results_to_db(self.project, self.case_name, + self.start_date, + self.stop_date, + self.criteria, self.details)) + readline = 0 + test_ip = ['10.1.23.4', '10.1.14.15', '10.1.16.15'] + + @staticmethod + def readline_side(): + if FunctestUtilsTesting.readline == \ + len(FunctestUtilsTesting.test_ip) - 1: + return False + FunctestUtilsTesting.readline += 1 + return FunctestUtilsTesting.test_ip[FunctestUtilsTesting.readline] + + # TODO: get_resolvconf_ns + @mock.patch('functest.utils.functest_utils.dns.resolver.Resolver') + def test_get_resolvconf_ns_default(self, mock_dns_resolve): + attrs = {'query.return_value': ["test"]} + mock_dns_resolve.configure_mock(**attrs) + + m = mock.Mock() + attrs = {'readline.side_effect': self.readline_side} + m.configure_mock(**attrs) + + with mock.patch("__builtin__.open") as mo: + mo.return_value = m + self.assertEqual(functest_utils.get_resolvconf_ns(), + self.test_ip[1:]) + + def _get_environ(self, var): + if var == 'INSTALLER_TYPE': + return self.installer + elif var == 'DEPLOY_SCENARIO': + return self.scenario + return var + + def test_get_ci_envvars_default(self): + with mock.patch('os.environ.get', + side_effect=self._get_environ): + dic = {"installer": self.installer, + "scenario": self.scenario} + self.assertDictEqual(functest_utils.get_ci_envvars(), dic) + + def cmd_readline(self): + return 'test_value\n' + + @mock.patch('functest.utils.functest_utils.logger.error') + @mock.patch('functest.utils.functest_utils.logger.info') + def test_execute_command_args_present_with_error(self, mock_logger_info, + mock_logger_error): + with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ + as mock_subproc_open, \ + mock.patch('__builtin__.open', mock.mock_open()) as mopen: + + FunctestUtilsTesting.readline = 0 + + mock_obj = mock.Mock() + attrs = {'readline.side_effect': self.cmd_readline()} + mock_obj.configure_mock(**attrs) + + mock_obj2 = mock.Mock() + attrs = {'stdout': mock_obj, 'wait.return_value': 1} + mock_obj2.configure_mock(**attrs) + + mock_subproc_open.return_value = mock_obj2 + + resp = functest_utils.execute_command(self.cmd, info=True, + error_msg=self.error_msg, + verbose=True, + output_file=self.output_file) + self.assertEqual(resp, 1) + msg_exec = ("Executing command: '%s'" % self.cmd) + mock_logger_info.assert_called_once_with(msg_exec) + mopen.assert_called_once_with(self.output_file, "w") + mock_logger_error.assert_called_once_with(self.error_msg) + + @mock.patch('functest.utils.functest_utils.logger.info') + def test_execute_command_args_present_with_success(self, mock_logger_info, + ): + with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ + as mock_subproc_open, \ + mock.patch('__builtin__.open', mock.mock_open()) as mopen: + + FunctestUtilsTesting.readline = 0 + + mock_obj = mock.Mock() + attrs = {'readline.side_effect': self.cmd_readline()} + mock_obj.configure_mock(**attrs) + + mock_obj2 = mock.Mock() + attrs = {'stdout': mock_obj, 'wait.return_value': 0} + mock_obj2.configure_mock(**attrs) + + mock_subproc_open.return_value = mock_obj2 + + resp = functest_utils.execute_command(self.cmd, info=True, + error_msg=self.error_msg, + verbose=True, + output_file=self.output_file) + self.assertEqual(resp, 0) + msg_exec = ("Executing command: '%s'" % self.cmd) + mock_logger_info.assert_called_once_with(msg_exec) + mopen.assert_called_once_with(self.output_file, "w") + + @mock.patch('functest.utils.functest_utils.logger.info') + def test_execute_command_args_missing_with_success(self, mock_logger_info, + ): + with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ + as mock_subproc_open: + + FunctestUtilsTesting.readline = 2 + + mock_obj = mock.Mock() + attrs = {'readline.side_effect': self.cmd_readline()} + mock_obj.configure_mock(**attrs) + + mock_obj2 = mock.Mock() + attrs = {'stdout': mock_obj, 'wait.return_value': 0} + mock_obj2.configure_mock(**attrs) + + mock_subproc_open.return_value = mock_obj2 + + resp = functest_utils.execute_command(self.cmd, info=False, + error_msg="", + verbose=False, + output_file=None) + self.assertEqual(resp, 0) + + @mock.patch('functest.utils.functest_utils.logger.error') + def test_execute_command_args_missing_with_error(self, mock_logger_error, + ): + with mock.patch('functest.utils.functest_utils.subprocess.Popen') \ + as mock_subproc_open: + + FunctestUtilsTesting.readline = 2 + mock_obj = mock.Mock() + attrs = {'readline.side_effect': self.cmd_readline()} + mock_obj.configure_mock(**attrs) + + mock_obj2 = mock.Mock() + attrs = {'stdout': mock_obj, 'wait.return_value': 1} + mock_obj2.configure_mock(**attrs) + + mock_subproc_open.return_value = mock_obj2 + + resp = functest_utils.execute_command(self.cmd, info=False, + error_msg="", + verbose=False, + output_file=None) + self.assertEqual(resp, 1) + + def _get_functest_config(self, var): + return var + + @mock.patch('functest.utils.functest_utils.logger.error') + def test_get_dict_by_test(self, mock_logger_error): + with mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.utils.functest_utils.yaml.safe_load') \ + as mock_yaml, \ + mock.patch('functest.utils.functest_utils.get_testcases_' + 'file_dir'): + mock_obj = mock.Mock() + attrs = {'get.return_value': [{'testcases': [self.testcase_dict]}]} + mock_obj.configure_mock(**attrs) + + mock_yaml.return_value = mock_obj + + self.assertDictEqual(functest_utils. + get_dict_by_test(self.testname), + self.testcase_dict) + + @mock.patch('functest.utils.functest_utils.get_dict_by_test') + def test_get_criteria_by_test_default(self, mock_get_dict_by_test): + mock_get_dict_by_test.return_value = self.testcase_dict + self.assertEqual(functest_utils.get_criteria_by_test(self.testname), + self.criteria) + + @mock.patch('functest.utils.functest_utils.get_dict_by_test') + def test_get_criteria_by_test_failed(self, mock_get_dict_by_test): + mock_get_dict_by_test.return_value = None + self.assertIsNone(functest_utils.get_criteria_by_test(self.testname)) + + def test_get_parameter_from_yaml_failed(self): + self.file_yaml['general'] = None + with mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.utils.functest_utils.yaml.safe_load') \ + as mock_yaml, \ + self.assertRaises(ValueError) as excep: + mock_yaml.return_value = self.file_yaml + functest_utils.get_parameter_from_yaml(self.parameter, + self.test_file) + self.assertTrue(("The parameter %s is not" + " defined in config_functest.yaml" % + self.parameter) in excep.exception) + + def test_get_parameter_from_yaml_default(self): + with mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.utils.functest_utils.yaml.safe_load') \ + as mock_yaml: + mock_yaml.return_value = self.file_yaml + self.assertEqual(functest_utils. + get_parameter_from_yaml(self.parameter, + self.test_file), + 'test_image_name') + + @mock.patch('functest.utils.functest_utils.get_parameter_from_yaml') + def test_get_functest_config_default(self, mock_get_parameter_from_yaml): + with mock.patch.dict(os.environ, + {'CONFIG_FUNCTEST_YAML': self.config_yaml}): + functest_utils.get_functest_config(self.parameter) + mock_get_parameter_from_yaml. \ + assert_called_once_with(self.parameter, + self.config_yaml) + + def test_check_success_rate_default(self): + with mock.patch('functest.utils.functest_utils.get_criteria_by_test') \ + as mock_criteria: + mock_criteria.return_value = self.criteria + resp = functest_utils.check_success_rate(self.case_name, + self.success_rate) + self.assertEqual(resp, 'PASS') + + def test_check_success_rate_failed(self): + with mock.patch('functest.utils.functest_utils.get_criteria_by_test') \ + as mock_criteria: + mock_criteria.return_value = self.criteria + resp = functest_utils.check_success_rate(self.case_name, + 3.0) + self.assertEqual(resp, 'FAIL') + + # TODO: merge_dicts + + def test_get_testcases_file_dir(self): + resp = functest_utils.get_testcases_file_dir() + self.assertEqual(resp, + "/home/opnfv/repos/functest/" + "functest/ci/testcases.yaml") + + def test_get_functest_yaml(self): + with mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.utils.functest_utils.yaml.safe_load') \ + as mock_yaml: + mock_yaml.return_value = self.file_yaml + resp = functest_utils.get_functest_yaml() + self.assertEqual(resp, self.file_yaml) + + @mock.patch('functest.utils.functest_utils.logger.info') + def test_print_separator(self, mock_logger_info): + functest_utils.print_separator() + mock_logger_info.assert_called_once_with("=======================" + "=======================") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/utils/test_utils.py b/functest/tests/unit/utils/test_utils.py deleted file mode 100644 index 8b6c5e1b9..000000000 --- a/functest/tests/unit/utils/test_utils.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016 Orange and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 - -import logging -import mock -import unittest -import urllib2 - -from functest.utils import functest_utils - - -class FunctestUtilsTesting(unittest.TestCase): - - logging.disable(logging.CRITICAL) - - def setUp(self): - self.url = 'http://www.opnfv.org/' - self.timeout = 5 - - @mock.patch('urllib2.urlopen', - side_effect=urllib2.URLError('no host given')) - def test_check_internet_connectivity_failed(self, mock_method): - self.assertFalse(functest_utils.check_internet_connectivity()) - mock_method.assert_called_once_with(self.url, timeout=self.timeout) - - @mock.patch('urllib2.urlopen') - def test_check_internet_connectivity_default(self, mock_method): - self.assertTrue(functest_utils.check_internet_connectivity()) - mock_method.assert_called_once_with(self.url, timeout=self.timeout) - - @mock.patch('urllib2.urlopen') - def test_check_internet_connectivity_debian(self, mock_method): - self.url = "https://www.debian.org/" - self.assertTrue(functest_utils.check_internet_connectivity(self.url)) - mock_method.assert_called_once_with(self.url, timeout=self.timeout) - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/functest/utils/config.py b/functest/utils/config.py new file mode 100644 index 000000000..84166c1d3 --- /dev/null +++ b/functest/utils/config.py @@ -0,0 +1,35 @@ +import os + +import yaml + + +class Config(object): + def __init__(self): + if 'CONFIG_FUNCTEST_YAML' not in os.environ: + raise Exception('CONFIG_FUNCTEST_YAML not configed') + self.config_functest = os.environ['CONFIG_FUNCTEST_YAML'] + try: + with open(self.config_functest) as f: + self.functest_yaml = yaml.safe_load(f) + self._parse(None, self.functest_yaml) + except: + raise Exception('Parse {} failed'.format(self.config_functest)) + self._set_others() + + def _parse(self, attr_now, left_parametes): + for param_n, param_v in left_parametes.iteritems(): + attr_further = self._get_attr_further(attr_now, param_n) + if not isinstance(param_v, dict): + self.__setattr__(attr_further, param_v) + else: + self._parse(attr_further, param_v) + + def _get_attr_further(self, attr_now, next): + return attr_now if next == 'general' else ( + '{}_{}'.format(attr_now, next) if attr_now else next) + + def _set_others(self): + self.env_active = os.path.join(self.dir_functest_conf, "env_active") + + +CONF = Config() diff --git a/functest/utils/constants.py b/functest/utils/constants.py new file mode 100644 index 000000000..2e8eb3f44 --- /dev/null +++ b/functest/utils/constants.py @@ -0,0 +1,20 @@ +import config +import env + + +class Constants(object): + def __init__(self): + for attr_n, attr_v in config.CONF.__dict__.iteritems(): + self.__setattr__(attr_n, attr_v) + for env_n, env_v in env.ENV.__dict__.iteritems(): + self.__setattr__(env_n, env_v) + + +CONST = Constants() + +if __name__ == '__main__': + print CONST.__dict__ + print CONST.NODE_NAME + print CONST.vIMS_clearwater_blueprint_url + print CONST.vIMS_clearwater_blueprint_file_name + print CONST.vIMS_clearwater_blueprint_name diff --git a/functest/utils/env.py b/functest/utils/env.py new file mode 100644 index 000000000..fa5245fb6 --- /dev/null +++ b/functest/utils/env.py @@ -0,0 +1,41 @@ +import os +import re + +default_envs = { + 'NODE_NAME': 'unknown_pod', + 'CI_DEBUG': 'true', + 'DEPLOY_SCENARIO': 'os-nosdn-nofeature-noha', + 'DEPLOY_TYPE': 'virt', + 'INSTALLER_TYPE': None, + 'INSTALLER_IP': None, + 'BUILD_TAG': None, + 'OS_ENDPOINT_TYPE': None, + 'OS_AUTH_URL': None +} + + +class Environment(object): + + def __init__(self): + for k, v in os.environ.iteritems(): + self.__setattr__(k, v) + for k, v in default_envs.iteritems(): + if k not in os.environ: + self.__setattr__(k, v) + self._set_ci_run() + self._set_ci_loop() + + def _set_ci_run(self): + if self.BUILD_TAG: + self.IS_CI_RUN = True + else: + self.IS_CI_RUN = False + + def _set_ci_loop(self): + if self.BUILD_TAG and re.search("daily", self.BUILD_TAG): + self.CI_LOOP = "daily" + else: + self.CI_LOOP = "weekly" + + +ENV = Environment() diff --git a/functest/utils/functest_constants.py b/functest/utils/functest_constants.py index 2664ace1f..ac9d77c87 100644 --- a/functest/utils/functest_constants.py +++ b/functest/utils/functest_constants.py @@ -7,8 +7,9 @@ # http://www.apache.org/licenses/LICENSE-2.0 # import os -import functest.utils.functest_utils as ft_utils + import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils logger = ft_logger.Logger("functest_constants").getLogger() @@ -60,25 +61,25 @@ def get_value(functest_config_key, env_variable): return constant -HOME = get_value('general.directories.dir_home', 'HOME') -REPOS_DIR = get_value('general.directories.dir_repos', 'REPOS_DIR') -FUNCTEST_BASE_DIR = get_value('general.directories.dir_functest', +HOME = get_value('general.dir.home', 'HOME') +REPOS_DIR = get_value('general.dir.repos', 'REPOS_DIR') +FUNCTEST_BASE_DIR = get_value('general.dir.functest', 'FUNCTEST_BASE_DIR') -FUNCTEST_REPO_DIR = get_value('general.directories.dir_repo_functest', +FUNCTEST_REPO_DIR = get_value('general.dir.repo_functest', 'FUNCTEST_REPO_DIR') -FUNCTEST_TEST_DIR = get_value('general.directories.dir_functest_test', +FUNCTEST_TEST_DIR = get_value('general.dir.functest_test', 'FUNCTEST_TEST_DIR') -FUNCTEST_CONF_DIR = get_value('general.directories.dir_functest_conf', +FUNCTEST_CONF_DIR = get_value('general.dir.functest_conf', 'FUNCTEST_CONF_DIR') -FUNCTEST_DATA_DIR = get_value('general.directories.dir_functest_data', +FUNCTEST_DATA_DIR = get_value('general.dir.functest_data', 'FUNCTEST_DATA_DIR') -FUNCTEST_RESULTS_DIR = get_value('general.directories.dir_results', +FUNCTEST_RESULTS_DIR = get_value('general.dir.results', 'FUNCTEST_RESULTS_DIR') FUNCTEST_TESTCASES_YAML = get_value('general.functest.testcases_yaml', 'FUNCTEST_TESTCASES_YAML') RALLY_DEPLOYMENT_NAME = get_value('rally.deployment_name', 'RALLY_DEPLOYMENT_NAME') -TEMPEST_REPO_DIR = get_value('general.directories.dir_repo_tempest', +TEMPEST_REPO_DIR = get_value('general.dir.repo_tempest', 'TEMPEST_REPO_DIR') ENV_FILE = os.path.join(FUNCTEST_CONF_DIR, "env_active") @@ -87,22 +88,22 @@ OPENSTACK_CREDS = get_value('general.openstack.creds', 'creds') OPENSTACK_SNAPSHOT_FILE = get_value('general.openstack.snapshot_file', 'OPENSTACK_SNAPSHOT_FILE') -DOMINO_REPO_DIR = get_value('general.directories.dir_repo_domino', +DOMINO_REPO_DIR = get_value('general.dir.repo_domino', 'DOMINO_REPO_DIR') -SDNVPN_REPO_DIR = get_value('general.directories.dir_repo_sdnvpn', +SDNVPN_REPO_DIR = get_value('general.dir.repo_sdnvpn', 'SDNVPN_REPO_DIR') -SFC_REPO_DIR = get_value('general.directories.dir_repo_sfc', +SFC_REPO_DIR = get_value('general.dir.repo_sfc', 'SFC_REPO_DIR') ONOS_SFC_IMAGE_NAME = get_value('onos_sfc.image_name', 'ONOS_SFC_IMAGE_NAME') ONOS_SFC_IMAGE_FILENAME = get_value('onos_sfc.image_file_name', 'ONOS_SFC_IMAGE_FILENAME') -ONOS_SFC_RELATIVE_PATH = get_value('general.directories.dir_onos_sfc', +ONOS_SFC_RELATIVE_PATH = get_value('general.dir.dir_onos_sfc', 'ONOS_SFC_RELATIVE_PATH') ONOS_SFC_IMAGE_BASE_URL = get_value('onos_sfc.image_base_url', 'ONOS_SFC_IMAGE_BASE_URL') -RALLY_RELATIVE_PATH = get_value('general.directories.dir_rally', +RALLY_RELATIVE_PATH = get_value('general.dir.rally', 'RALLY_RELATIVE_PATH') RALLY_PRIVATE_NET_NAME = get_value('rally.network_name', 'RALLY_PRIVATE_NET_NAME') @@ -111,7 +112,7 @@ RALLY_PRIVATE_SUBNET_NAME = get_value('rally.subnet_name', RALLY_PRIVATE_SUBNET_CIDR = get_value('rally.subnet_cidr', 'RALLY_PRIVATE_SUBNET_CIDR') RALLY_ROUTER_NAME = get_value('rally.router_name', 'RALLY_ROUTER_NAME') -RALLY_INSTALLATION_DIR = get_value('general.directories.dir_rally_inst', +RALLY_INSTALLATION_DIR = get_value('general.dir.rally_inst', 'RALLY_INSTALLATION_DIR') GLANCE_IMAGE_NAME = get_value('general.openstack.image_name', 'GLANCE_IMAGE_NAME') @@ -149,24 +150,24 @@ TEMPEST_USE_CUSTOM_IMAGES = get_value('tempest.use_custom_images', 'TEMPEST_USE_CUSTOM_IMAGES') TEMPEST_USE_CUSTOM_FLAVORS = get_value('tempest.use_custom_flavors', 'TEMPEST_USE_CUSTOM_FLAVORS') -TEMPEST_TEST_LIST_DIR = get_value('general.directories.dir_tempest_cases', +TEMPEST_TEST_LIST_DIR = get_value('general.dir.tempest_cases', 'TEMPEST_TEST_LIST_DIR') NAME_VM_1 = get_value('vping.vm_name_1', 'NAME_VM_1') NAME_VM_2 = get_value('vping.vm_name_2', 'NAME_VM_2') PING_TIMEOUT = get_value('vping.ping_timeout', 'PING_TIMEOUT') VPING__IMAGE_NAME = get_value('vping.image_name', 'VPING__IMAGE_NAME') VPING_VM_FLAVOR = get_value('vping.vm_flavor', 'VPING_VM_FLAVOR') -VPING_PRIVATE_NET_NAME = get_value('vping.vping_private_net_name', +VPING_PRIVATE_NET_NAME = get_value('vping.private_net_name', 'VPING_PRIVATE_NET_NAME') -VPING_PRIVATE_SUBNET_NAME = get_value('vping.vping_private_subnet_name', +VPING_PRIVATE_SUBNET_NAME = get_value('vping.private_subnet_name', 'VPING_PRIVATE_SUBNET_NAME') -VPING_PRIVATE_SUBNET_CIDR = get_value('vping.vping_private_subnet_cidr', +VPING_PRIVATE_SUBNET_CIDR = get_value('vping.private_subnet_cidr', 'VPING_PRIVATE_SUBNET_CIDR') -VPING_ROUTER_NAME = get_value('vping.vping_router_name', +VPING_ROUTER_NAME = get_value('vping.router_name', 'VPING_ROUTER_NAME') -VPING_SECGROUP_NAME = get_value('vping.vping_sg_name', +VPING_SECGROUP_NAME = get_value('vping.sg_name', 'VPING_SECGROUP_NAME') -VPING_SECGROUP_DESCR = get_value('vping.vping_sg_descr', +VPING_SECGROUP_DESCR = get_value('vping.sg_desc', 'VPING_SECGROUP_DESCR') ONOSBENCH_USERNAME = get_value('ONOS.general.onosbench_username', 'ONOSBENCH_USERNAME') @@ -192,7 +193,7 @@ ONOS_INSTALLER_MASTER_USERNAME = get_value( ONOS_INSTALLER_MASTER_PASSWORD = get_value( 'ONOS.environment.installer_master_password', 'ONOS_INSTALLER_MASTER_PASSWORD') -PROMISE_REPO_DIR = get_value('general.directories.dir_repo_promise', +PROMISE_REPO_DIR = get_value('general.dir.dir_repo_promise', 'PROMISE_REPO_DIR') PROMISE_TENANT_NAME = get_value('promise.tenant_name', 'PROMISE_TENANT_NAME') @@ -217,32 +218,32 @@ PROMISE_SUBNET_CIDR = get_value('promise.subnet_cidr', 'PROMISE_SUBNET_CIDR') PROMISE_ROUTER_NAME = get_value('promise.router_name', 'PROMISE_ROUTER_NAME') -DOCTOR_REPO_DIR = get_value('general.directories.dir_repo_doctor', +DOCTOR_REPO_DIR = get_value('general.dir.dir_repo_doctor', 'DOCTOR_REPO_DIR') -COPPER_REPO_DIR = get_value('general.directories.dir_repo_copper', +COPPER_REPO_DIR = get_value('general.dir.repo_copper', 'COPPER_REPO_DIR') -EXAMPLE_INSTANCE_NAME = get_value('example.example_vm_name', +EXAMPLE_INSTANCE_NAME = get_value('example.vm_name', 'EXAMPLE_INSTANCE_NAME') -EXAMPLE_FLAVOR = get_value('example.example_flavor', 'EXAMPLE_FLAVOR') -EXAMPLE_IMAGE_NAME = get_value('example.example_image_name', +EXAMPLE_FLAVOR = get_value('example.flavor', 'EXAMPLE_FLAVOR') +EXAMPLE_IMAGE_NAME = get_value('example.image_name', 'EXAMPLE_IMAGE_NAME') -EXAMPLE_PRIVATE_NET_NAME = get_value('example.example_private_net_name', +EXAMPLE_PRIVATE_NET_NAME = get_value('example.private_net_name', 'EXAMPLE_PRIVATE_NET_NAME') EXAMPLE_PRIVATE_SUBNET_NAME = get_value( - 'example.example_private_subnet_name', + 'example.private_subnet_name', 'EXAMPLE_PRIVATE_SUBNET_NAME') EXAMPLE_PRIVATE_SUBNET_CIDR = get_value( - 'example.example_private_subnet_cidr', + 'example.private_subnet_cidr', 'EXAMPLE_PRIVATE_SUBNET_CIDR') -EXAMPLE_ROUTER_NAME = get_value('example.example_router_name', +EXAMPLE_ROUTER_NAME = get_value('example.router_name', 'EXAMPLE_ROUTER_NAME') -EXAMPLE_SECGROUP_NAME = get_value('example.example_sg_name', +EXAMPLE_SECGROUP_NAME = get_value('example.sg_name', 'EXAMPLE_SECGROUP_NAME') -EXAMPLE_SECGROUP_DESCR = get_value('example.example_sg_descr', +EXAMPLE_SECGROUP_DESCR = get_value('example.sg_desc', 'EXAMPLE_SECGROUP_DESCR') -VIMS_DATA_DIR = get_value('general.directories.dir_vIMS_data', +VIMS_DATA_DIR = get_value('general.dir.dir_vIMS_data', 'VIMS_DATA_DIR') -VIMS_TEST_DIR = get_value('general.directories.dir_repo_vims_test', +VIMS_TEST_DIR = get_value('general.dir.dir_repo_vims_test', 'VIMS_TEST_DIR') VIMS_TENANT_NAME = get_value('vIMS.general.tenant_name', 'VIMS_TENANT_NAME') @@ -260,5 +261,5 @@ CW_DEPLOYMENT_NAME = get_value('vIMS.clearwater.deployment-name', CW_INPUTS = get_value('vIMS.clearwater.inputs', 'CW_INPUTS') CW_REQUIERMENTS = get_value('vIMS.clearwater.requierments', 'CW_REQUIERMENTS') -PARSER_REPO_DIR = get_value('general.directories.dir_repo_parser', +PARSER_REPO_DIR = get_value('general.dir.repo_parser', 'PARSER_REPO_DIR') diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py index b1e4d3cdb..1879e6943 100644 --- a/functest/utils/functest_utils.py +++ b/functest/utils/functest_utils.py @@ -7,12 +7,14 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 # +import functools import json import os import re import shutil import subprocess import sys +import time import urllib2 from datetime import datetime as dt @@ -21,9 +23,6 @@ import requests import yaml from git import Repo -import time -import functools - import functest.utils.functest_logger as ft_logger logger = ft_logger.Logger("functest_utils").getLogger() @@ -321,26 +320,6 @@ def execute_command(cmd, info=False, error_msg="", return returncode -def get_deployment_dir(): - """ - Returns current Rally deployment directory - """ - deployment_name = get_functest_config('rally.deployment_name') - rally_dir = get_functest_config('general.directories.dir_rally_inst') - cmd = ("rally deployment list | awk '/" + deployment_name + - "/ {print $2}'") - p = subprocess.Popen(cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - deployment_uuid = p.stdout.readline().rstrip() - if deployment_uuid == "": - logger.error("Rally deployment not found.") - exit(-1) - deployment_dir = (rally_dir + "/tempest/for-deployment-" + - deployment_uuid) - return deployment_dir - - def get_dict_by_test(testname): with open(get_testcases_file_dir()) as f: testcases_yaml = yaml.safe_load(f) diff --git a/functest/utils/openstack_clean.py b/functest/utils/openstack_clean.py index 949eee90f..15a8f33d5 100755 --- a/functest/utils/openstack_clean.py +++ b/functest/utils/openstack_clean.py @@ -9,6 +9,8 @@ # - Neutron networks, subnets and ports # - Routers # - Users and tenants +# - Tacker VNFDs and VNFs +# - Tacker SFCs and SFC classifiers # # Author: # jose.lausuch@ericsson.com @@ -21,14 +23,16 @@ # import time + +import yaml + import functest.utils.functest_logger as ft_logger import functest.utils.openstack_utils as os_utils -import yaml -import functest.utils.functest_constants as ft_constants +from functest.utils.constants import CONST logger = ft_logger.Logger("openstack_clean").getLogger() -OS_SNAPSHOT_FILE = ft_constants.OPENSTACK_SNAPSHOT_FILE +OS_SNAPSHOT_FILE = CONST.openstack_snapshot_file def separator(): @@ -105,7 +109,7 @@ def remove_volumes(cinder_client, default_volumes): for volume in volumes: volume_id = getattr(volume, 'id') - volume_name = getattr(volume, 'display_name') + volume_name = getattr(volume, 'name') logger.debug("'%s', ID=%s " % (volume_name, volume_id)) if (volume_id not in default_volumes and volume_name not in default_volumes.values()): diff --git a/functest/utils/openstack_snapshot.py b/functest/utils/openstack_snapshot.py index 4be1af443..e64030f75 100755 --- a/functest/utils/openstack_snapshot.py +++ b/functest/utils/openstack_snapshot.py @@ -20,15 +20,16 @@ # http://www.apache.org/licenses/LICENSE-2.0 # +import yaml + import functest.utils.functest_logger as ft_logger import functest.utils.openstack_utils as os_utils -import yaml -import functest.utils.functest_constants as ft_constants +from functest.utils.constants import CONST logger = ft_logger.Logger("openstack_snapshot").getLogger() -OS_SNAPSHOT_FILE = ft_constants.OPENSTACK_SNAPSHOT_FILE +OS_SNAPSHOT_FILE = CONST.openstack_snapshot_file def separator(): @@ -62,7 +63,7 @@ def get_volumes(cinder_client): volumes = os_utils.get_volumes(cinder_client) if volumes is not None: for volume in volumes: - dic_volumes.update({volume.id: volume.display_name}) + dic_volumes.update({volume.id: volume.name}) return {'volumes': dic_volumes} diff --git a/functest/utils/openstack_tacker.py b/functest/utils/openstack_tacker.py index 6ab056683..f17b421e8 100644..100755 --- a/functest/utils/openstack_tacker.py +++ b/functest/utils/openstack_tacker.py @@ -21,7 +21,7 @@ logger = ft_logger.Logger("tacker_utils").getLogger() def get_tacker_client(): - creds_tacker = os_utils.get_credentials('tacker') + creds_tacker = os_utils.get_credentials() return tackerclient.Client(**creds_tacker) diff --git a/functest/utils/openstack_utils.py b/functest/utils/openstack_utils.py index 15dc87c38..64f18504d 100755 --- a/functest/utils/openstack_utils.py +++ b/functest/utils/openstack_utils.py @@ -14,16 +14,21 @@ import subprocess import sys import time +from keystoneauth1 import loading +from keystoneauth1 import session from cinderclient import client as cinderclient -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_utils as ft_utils from glanceclient import client as glanceclient -from keystoneclient.v2_0 import client as keystoneclient -from neutronclient.v2_0 import client as neutronclient from novaclient import client as novaclient +from keystoneclient import client as keystoneclient +from neutronclient.neutron import client as neutronclient + +import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils logger = ft_logger.Logger("openstack_utils").getLogger() +DEFAULT_API_VERSION = '2' + # ********************************************* # CREDENTIALS @@ -37,88 +42,72 @@ class MissingEnvVar(Exception): return str.format("Please set the mandatory env var: {}", self.var) +def is_keystone_v3(): + keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION') + if (keystone_api_version is None or + keystone_api_version == '2'): + return False + else: + return True + + +def get_rc_env_vars(): + env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD'] + if is_keystone_v3(): + env_vars.extend(['OS_PROJECT_NAME', + 'OS_USER_DOMAIN_NAME', + 'OS_PROJECT_DOMAIN_NAME']) + else: + env_vars.extend(['OS_TENANT_NAME']) + return env_vars + + def check_credentials(): """ Check if the OpenStack credentials (openrc) are sourced """ - env_vars = ['OS_AUTH_URL', 'OS_USERNAME', 'OS_PASSWORD', 'OS_TENANT_NAME'] + env_vars = get_rc_env_vars() return all(map(lambda v: v in os.environ and os.environ[v], env_vars)) -def get_credentials(service): - """Returns a creds dictionary filled with the following keys: - * username - * password/api_key (depending on the service) - * tenant_name/project_id (depending on the service) - * auth_url - :param service: a string indicating the name of the service - requesting the credentials. +def get_env_cred_dict(): + env_cred_dict = { + 'OS_USERNAME': 'username', + 'OS_PASSWORD': 'password', + 'OS_AUTH_URL': 'auth_url', + 'OS_TENANT_NAME': 'tenant_name', + 'OS_USER_DOMAIN_NAME': 'user_domain_name', + 'OS_PROJECT_DOMAIN_NAME': 'project_domain_name', + 'OS_PROJECT_NAME': 'project_name', + 'OS_ENDPOINT_TYPE': 'endpoint_type', + 'OS_REGION_NAME': 'region_name' + } + return env_cred_dict + + +def get_credentials(other_creds={}): + """Returns a creds dictionary filled with parsed from env """ creds = {} + env_vars = get_rc_env_vars() + env_cred_dict = get_env_cred_dict() - keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION') - if (keystone_api_version is None or - keystone_api_version == '2'): - keystone_v3 = False - tenant_env = 'OS_TENANT_NAME' - tenant = 'tenant_name' - else: - keystone_v3 = True - tenant_env = 'OS_PROJECT_NAME' - tenant = 'project_name' - - # Check that the env vars exists: - envvars = ('OS_USERNAME', 'OS_PASSWORD', 'OS_AUTH_URL', tenant_env) - for envvar in envvars: + for envvar in env_vars: if os.getenv(envvar) is None: raise MissingEnvVar(envvar) + else: + creds_key = env_cred_dict.get(envvar) + creds.update({creds_key: os.getenv(envvar)}) + + if 'tenant' in other_creds.keys(): + if is_keystone_v3(): + tenant = 'project_name' + else: + tenant = 'tenant_name' + other_creds[tenant] = other_creds.pop('tenant') + + creds.update(other_creds) - # Unfortunately, each of the OpenStack client will request slightly - # different entries in their credentials dict. - if service.lower() in ("nova", "cinder"): - password = "api_key" - tenant = "project_id" - else: - password = "password" - - # The most common way to pass these info to the script is to do it through - # environment variables. - creds.update({ - "username": os.environ.get("OS_USERNAME"), - password: os.environ.get("OS_PASSWORD"), - "auth_url": os.environ.get("OS_AUTH_URL"), - tenant: os.environ.get(tenant_env) - }) - if keystone_v3: - if os.getenv('OS_USER_DOMAIN_NAME') is not None: - creds.update({ - "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME') - }) - if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None: - creds.update({ - "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME') - }) - - if os.getenv('OS_ENDPOINT_TYPE') is not None: - creds.update({ - "endpoint_type": os.environ.get("OS_ENDPOINT_TYPE") - }) - if os.getenv('OS_REGION_NAME') is not None: - creds.update({ - "region_name": os.environ.get("OS_REGION_NAME") - }) - cacert = os.environ.get("OS_CACERT") - if cacert is not None: - # each openstack client uses differnt kwargs for this - creds.update({"cacert": cacert, - "ca_cert": cacert, - "https_ca_cert": cacert, - "https_cacert": cacert, - "ca_file": cacert}) - creds.update({"insecure": "True", "https_insecure": "True"}) - if not os.path.isfile(cacert): - logger.info("WARNING: The 'OS_CACERT' environment variable is " - "set to %s but the file does not exist." % cacert) return creds @@ -132,66 +121,121 @@ def source_credentials(rc_file): def get_credentials_for_rally(): - creds = get_credentials("keystone") - keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION') - if (keystone_api_version is None or - keystone_api_version == '2'): - admin_keys = ['username', 'tenant_name', 'password'] - else: - admin_keys = ['username', 'password', 'user_domain_name', - 'project_name', 'project_domain_name'] + creds = get_credentials() + env_cred_dict = get_env_cred_dict() + rally_conf = {"type": "ExistingCloud", "admin": {}} + for key in creds: + if key == 'auth_url': + rally_conf[key] = creds[key] + else: + rally_conf['admin'][key] = creds[key] endpoint_types = [('internalURL', 'internal'), ('publicURL', 'public'), ('adminURL', 'admin')] - if 'endpoint_type' in creds.keys(): + + endpoint_type = os.getenv('OS_ENDPOINT_TYPE') + if endpoint_type is not None: + cred_key = env_cred_dict.get('OS_ENDPOINT_TYPE') for k, v in endpoint_types: - if creds['endpoint_type'] == k: - creds['endpoint_type'] = v - rally_conf = {"type": "ExistingCloud", "admin": {}} - for key in creds: - if key in admin_keys: - rally_conf['admin'][key] = creds[key] - else: - rally_conf[key] = creds[key] + if endpoint_type == k: + rally_conf[cred_key] = v + + region_name = os.getenv('OS_REGION_NAME') + if region_name is not None: + cred_key = env_cred_dict.get('OS_REGION_NAME') + rally_conf[cred_key] = region_name return rally_conf +def get_session_auth(other_creds={}): + loader = loading.get_plugin_loader('password') + creds = get_credentials(other_creds) + auth = loader.load_from_options(**creds) + return auth + + +def get_endpoint(service_type, endpoint_type='publicURL'): + auth = get_session_auth() + return get_session().get_endpoint(auth=auth, + service_type=service_type, + endpoint_type=endpoint_type) + + +def get_session(other_creds={}): + auth = get_session_auth(other_creds) + return session.Session(auth=auth) + + # ********************************************* # CLIENTS # ********************************************* -def get_keystone_client(): - creds_keystone = get_credentials("keystone") - return keystoneclient.Client(**creds_keystone) +def get_keystone_client_version(): + api_version = os.getenv('OS_IDENTITY_API_VERSION') + if api_version is not None: + logger.info("OS_IDENTITY_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION + + +def get_keystone_client(other_creds={}): + sess = get_session(other_creds) + return keystoneclient.Client(get_keystone_client_version(), session=sess) + +def get_nova_client_version(): + api_version = os.getenv('OS_COMPUTE_API_VERSION') + if api_version is not None: + logger.info("OS_COMPUTE_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION -def get_nova_client(): - creds_nova = get_credentials("nova") - return novaclient.Client('2', **creds_nova) +def get_nova_client(other_creds={}): + sess = get_session(other_creds) + return novaclient.Client(get_nova_client_version(), session=sess) -def get_cinder_client(): - creds_cinder = get_credentials("cinder") - creds_cinder.update({ - "service_type": "volume" - }) - return cinderclient.Client('2', **creds_cinder) +def get_cinder_client_version(): + api_version = os.getenv('OS_VOLUME_API_VERSION') + if api_version is not None: + logger.info("OS_VOLUME_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION -def get_neutron_client(): - creds_neutron = get_credentials("neutron") - return neutronclient.Client(**creds_neutron) +def get_cinder_client(other_creds={}): + sess = get_session(other_creds) + return cinderclient.Client(get_cinder_client_version(), session=sess) -def get_glance_client(): - keystone_client = get_keystone_client() - glance_endpoint_type = 'publicURL' - os_endpoint_type = os.getenv('OS_ENDPOINT_TYPE') - if os_endpoint_type is not None: - glance_endpoint_type = os_endpoint_type - glance_endpoint = keystone_client.service_catalog.url_for( - service_type='image', endpoint_type=glance_endpoint_type) - return glanceclient.Client(1, glance_endpoint, - token=keystone_client.auth_token) + +def get_neutron_client_version(): + api_version = os.getenv('OS_NETWORK_API_VERSION') + if api_version is not None: + logger.info("OS_NETWORK_API_VERSION is set in env as '%s'", + api_version) + return api_version + return DEFAULT_API_VERSION + + +def get_neutron_client(other_creds={}): + sess = get_session(other_creds) + return neutronclient.Client(get_neutron_client_version(), session=sess) + + +def get_glance_client_version(): + api_version = os.getenv('OS_IMAGE_API_VERSION') + if api_version is not None: + logger.info("OS_IMAGE_API_VERSION is set in env as '%s'", api_version) + return api_version + return DEFAULT_API_VERSION + + +def get_glance_client(other_creds={}): + sess = get_session(other_creds) + return glanceclient.Client(get_glance_client_version(), session=sess) # ********************************************* @@ -473,13 +517,13 @@ def create_floating_ip(neutron_client): return {'fip_addr': fip_addr, 'fip_id': fip_id} -def add_floating_ip(nova_client, server_id, floatingip_id): +def add_floating_ip(nova_client, server_id, floatingip_addr): try: - nova_client.servers.add_floating_ip(server_id, floatingip_id) + nova_client.servers.add_floating_ip(server_id, floatingip_addr) return True except Exception, e: logger.error("Error [add_floating_ip(nova_client, '%s', '%s')]: %s" - % (server_id, floatingip_id, e)) + % (server_id, floatingip_addr, e)) return False @@ -1070,38 +1114,29 @@ def get_image_id(glance_client, image_name): def create_glance_image(glance_client, image_name, file_path, disk="qcow2", - container="bare", public=True): + container="bare", public="public"): if not os.path.isfile(file_path): logger.error("Error: file %s does not exist." % file_path) return None try: image_id = get_image_id(glance_client, image_name) if image_id != '': - if logger: - logger.info("Image %s already exists." % image_name) + logger.info("Image %s already exists." % image_name) else: - if logger: - logger.info("Creating image '%s' from '%s'..." % (image_name, - file_path)) - try: - properties = ft_utils.get_functest_config( - 'general.image_properties') - except ValueError: - # image properties are not configured - # therefore don't add any properties - properties = {} - with open(file_path) as fimage: - image = glance_client.images.create(name=image_name, - is_public=public, - disk_format=disk, - container_format=container, - properties=properties, - data=fimage) + logger.info("Creating image '%s' from '%s'..." % (image_name, + file_path)) + + image = glance_client.images.create(name=image_name, + visibility=public, + disk_format=disk, + container_format=container) image_id = image.id + with open(file_path) as image_data: + glance_client.images.upload(image_id, image_data) return image_id except Exception, e: logger.error("Error [create_glance_image(glance_client, '%s', '%s', " - "'%s')]: %s" % (image_name, file_path, str(public), e)) + "'%s')]: %s" % (image_name, file_path, public, e)) return None @@ -1218,7 +1253,10 @@ def delete_volume_type(cinder_client, volume_type): # ********************************************* def get_tenants(keystone_client): try: - tenants = keystone_client.tenants.list() + if is_keystone_v3(): + tenants = keystone_client.projects.list() + else: + tenants = keystone_client.tenants.list() return tenants except Exception, e: logger.error("Error [get_tenants(keystone_client)]: %s" % e) @@ -1235,7 +1273,7 @@ def get_users(keystone_client): def get_tenant_id(keystone_client, tenant_name): - tenants = keystone_client.tenants.list() + tenants = get_tenants(keystone_client) id = '' for t in tenants: if t.name == tenant_name: @@ -1245,7 +1283,7 @@ def get_tenant_id(keystone_client, tenant_name): def get_user_id(keystone_client, user_name): - users = keystone_client.users.list() + users = get_users(keystone_client) id = '' for u in users: if u.name == user_name: @@ -1266,9 +1304,16 @@ def get_role_id(keystone_client, role_name): def create_tenant(keystone_client, tenant_name, tenant_description): try: - tenant = keystone_client.tenants.create(tenant_name, - tenant_description, - enabled=True) + if is_keystone_v3(): + tenant = keystone_client.projects.create( + name=tenant_name, + description=tenant_description, + domain="default", + enabled=True) + else: + tenant = keystone_client.tenants.create(tenant_name, + tenant_description, + enabled=True) return tenant.id except Exception, e: logger.error("Error [create_tenant(keystone_client, '%s', '%s')]: %s" @@ -1279,9 +1324,18 @@ def create_tenant(keystone_client, tenant_name, tenant_description): def create_user(keystone_client, user_name, user_password, user_email, tenant_id): try: - user = keystone_client.users.create(user_name, user_password, - user_email, tenant_id, - enabled=True) + if is_keystone_v3(): + user = keystone_client.users.create(name=user_name, + password=user_password, + email=user_email, + project_id=tenant_id, + enabled=True) + else: + user = keystone_client.users.create(user_name, + user_password, + user_email, + tenant_id, + enabled=True) return user.id except Exception, e: logger.error("Error [create_user(keystone_client, '%s', '%s', '%s'" @@ -1292,7 +1346,12 @@ def create_user(keystone_client, user_name, user_password, def add_role_user(keystone_client, user_id, role_id, tenant_id): try: - keystone_client.roles.add_user_role(user_id, role_id, tenant_id) + if is_keystone_v3(): + keystone_client.roles.grant(role=role_id, + user=user_id, + project=tenant_id) + else: + keystone_client.roles.add_user_role(user_id, role_id, tenant_id) return True except Exception, e: logger.error("Error [add_role_user(keystone_client, '%s', '%s'" @@ -1302,7 +1361,10 @@ def add_role_user(keystone_client, user_id, role_id, tenant_id): def delete_tenant(keystone_client, tenant_id): try: - keystone_client.tenants.delete(tenant_id) + if is_keystone_v3(): + keystone_client.projects.delete(tenant_id) + else: + keystone_client.tenants.delete(tenant_id) return True except Exception, e: logger.error("Error [delete_tenant(keystone_client, '%s')]: %s" diff --git a/run_unit_tests.sh b/run_unit_tests.sh index ecd57d8ae..79d05d3d1 100755 --- a/run_unit_tests.sh +++ b/run_unit_tests.sh @@ -2,28 +2,6 @@ set -o errexit set -o pipefail -# ****************************** -# prepare the env for the tests -# ****************************** -# clean useless results dir -# should be done at the end -# but in case of crash during unit test -# clean it anyway -if [ -d "/home/opnfv/functest/results" ] -then - sudo rm -rf /home/opnfv/functest -fi - -# TODO clean that... -# Create log dir if needed -# log shall be disabled during unit tests -# fix to be done in Logger -echo "Create dummy log file...." -sudo mkdir -p /home/opnfv/functest/results/odl -sudo touch /home/opnfv/functest/results/functest.log -sudo touch /home/opnfv/functest/results/odl/stdout.txt -sudo chmod -Rf a+rw /home/opnfv - # Either Workspace is set (CI) if [ -z $WORKSPACE ] then @@ -53,8 +31,11 @@ export CONFIG_FUNCTEST_YAML=$(pwd)/functest/ci/config_functest.yaml nosetests --with-xunit \ --with-coverage \ --cover-erase \ + --cover-tests \ + --cover-package=functest.cli \ --cover-package=functest.core.testcase_base \ --cover-package=functest.opnfv_tests.sdn.odl.odl \ + --cover-package=functest.utils \ --cover-xml \ --cover-html \ functest/tests/unit @@ -62,13 +43,4 @@ rc=$? deactivate -# ******* -# clean -# ******* -# Clean useless logs -if [ -d "/home/opnfv/functest/results" ] -then - sudo rm -rf /home/opnfv/functest/results -fi - exit $rc @@ -1,25 +1,25 @@ -##############################################################################
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-from setuptools import setup, find_packages
-
-
-setup(
- name="functest",
- version="master",
- py_modules=['cli_base'],
- packages=find_packages(),
- include_package_data=True,
- package_data={
- },
- url="https://www.opnfv.org",
- entry_points={
- 'console_scripts': [
- 'functest=functest.cli.cli_base:cli'
- ],
- },
-)
+############################################################################## +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from setuptools import setup, find_packages + + +setup( + name="functest", + version="master", + py_modules=['cli_base'], + packages=find_packages(), + include_package_data=True, + package_data={ + }, + url="https://www.opnfv.org", + entry_points={ + 'console_scripts': [ + 'functest=functest.cli.cli_base:cli' + ], + }, +) diff --git a/test-requirements.txt b/test-requirements.txt index 8be8e2033..2bf297ba8 100755 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,6 +5,7 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 # +click==6.6 coverage==4.1 dnspython==1.15.0 gitpython==1.0.1 @@ -15,9 +16,10 @@ python-congressclient==1.5.0 python-keystoneclient==3.5.0 python-neutronclient==6.0.0 python-openstackclient==2.3.0 +python-tackerclient==0.7.0 pyyaml==3.10 requests==2.8.0 robotframework==2.9.1 robotframework-requests==0.3.8 robotframework-sshlibrary==2.1.1 -virtualenv==15.1.0
\ No newline at end of file +virtualenv==15.1.0 |