diff options
74 files changed, 3004 insertions, 1569 deletions
diff --git a/docker/Dockerfile b/docker/Dockerfile index 1cc2209e..de47e157 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -19,6 +19,7 @@ ARG ODL_TAG=release/beryllium-sr4 ARG OPENSTACK_TAG=stable/mitaka ARG KINGBIRD_TAG=0.2.2 ARG VIMS_TAG=stable +ARG VROUTER_TAG=stable ARG REPOS_DIR=/home/opnfv/repos ARG FUNCTEST_BASE_DIR=/home/opnfv/functest ARG FUNCTEST_CONF_DIR=${FUNCTEST_BASE_DIR}/conf @@ -77,26 +78,29 @@ RUN git config --global http.sslVerify false # OPNFV repositories RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/functest ${REPOS_DIR}/functest RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/copper ${REPOS_DIR}/copper -RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/moon ${REPOS_DIR}/moon RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/sdnvpn ${REPOS_DIR}/sdnvpn RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/domino ${REPOS_DIR}/domino RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/parser ${REPOS_DIR}/parser RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/doctor ${REPOS_DIR}/doctor RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/ovno ${REPOS_DIR}/ovno RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/promise ${REPOS_DIR}/promise +RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/netready ${REPOS_DIR}/netready +RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/barometer ${REPOS_DIR}/barometer RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/sfc ${REPOS_DIR}/sfc +RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/snaps ${REPOS_DIR}/snaps RUN git clone --depth 1 https://gerrit.opnfv.org/gerrit/securityscanning ${REPOS_DIR}/securityscanning RUN git clone --depth 1 https://gerrit.opnfv.org/gerrit/releng ${REPOS_DIR}/releng # OpenStack repositories RUN git clone --depth 1 -b $OPENSTACK_TAG https://github.com/openstack/networking-bgpvpn ${REPOS_DIR}/bgpvpn -#RUN git clone --depth 1 -b $KINGBIRD_TAG https://github.com/openstack/kingbird.git ${REPOS_DIR}/kingbird +RUN git clone --depth 1 -b $KINGBIRD_TAG https://github.com/openstack/kingbird.git ${REPOS_DIR}/kingbird RUN git clone --depth 1 -b $RALLY_TAG https://github.com/openstack/rally.git ${REPOS_DIR}/rally RUN git clone --depth 1 -b $TEMPEST_TAG https://github.com/openstack/tempest.git ${REPOS_DIR}/tempest # other repositories RUN git clone --depth 1 -b $ODL_TAG https://git.opendaylight.org/gerrit/p/integration/test.git ${REPOS_DIR}/odl_test RUN git clone --depth 1 -b $VIMS_TAG https://github.com/boucherv-orange/clearwater-live-test ${REPOS_VNFS_DIR}/vims-test +RUN git clone --depth 1 -b $VROUTER_TAG https://github.com/oolorg/opnfv-functest-vrouter.git ${REPOS_VNFS_DIR}/vrouter RUN git clone --depth 1 https://github.com/wuwenbin2/OnosSystemTest.git ${REPOS_DIR}/onos RUN pip install -r ${REPOS_DIR}/rally/requirements.txt @@ -109,6 +113,9 @@ RUN cd ${FUNCTEST_REPO_DIR} \ RUN cd ${RELENG_MODULE_DIR} \ && pip install . +RUN cd ${REPOS_DIR}/barometer \ + && pip install . + RUN find ${FUNCTEST_REPO_DIR} -name "*.py" \ -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 @@ -116,15 +123,12 @@ RUN find ${FUNCTEST_REPO_DIR} -name "*.py" \ RUN /bin/bash ${REPOS_DIR}/parser/tests/parser_install.sh ${REPOS_DIR} RUN ${REPOS_DIR}/rally/install_rally.sh --yes -ADD http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img ${FUNCTEST_BASE_DIR}/data/ -ADD http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-lxc.tar.gz ${FUNCTEST_BASE_DIR}/data/ -ADD http://205.177.226.237:9999/onosfw/firewall_block_image.img ${FUNCTEST_BASE_DIR}/data/ +RUN /bin/bash ${REPOS_DIR}/functest/docker/add_images.sh RUN gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 RUN curl -L https://get.rvm.io | bash -s stable # SNAPS integration -RUN git clone --depth 1 https://gerrit.cablelabs.com/snaps-provisioning ${REPOS_DIR}/snaps RUN pip install -e ${REPOS_DIR}/snaps/ # SFC integration @@ -135,8 +139,9 @@ RUN cd ${REPOS_DIR}/sfc && pip install . 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 + +# Kingbird integration +RUN cd ${REPOS_DIR}/kingbird && pip install -e . RUN /bin/bash -c ". /etc/profile.d/rvm.sh \ && cd ${REPOS_VNFS_DIR}/vims-test \ diff --git a/docker/Dockerfile.aarch64 b/docker/Dockerfile.aarch64 index a492baec..a469801f 100644 --- a/docker/Dockerfile.aarch64 +++ b/docker/Dockerfile.aarch64 @@ -78,7 +78,6 @@ RUN git config --global http.sslVerify false # OPNFV repositories RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/functest ${REPOS_DIR}/functest RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/copper ${REPOS_DIR}/copper -RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/moon ${REPOS_DIR}/moon RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/sdnvpn ${REPOS_DIR}/sdnvpn RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/domino ${REPOS_DIR}/domino RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/parser ${REPOS_DIR}/parser @@ -86,12 +85,13 @@ RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/doctor ${REPO RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/ovno ${REPOS_DIR}/ovno RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/promise ${REPOS_DIR}/promise RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/sfc ${REPOS_DIR}/sfc +RUN git clone --depth 1 -b $BRANCH https://gerrit.opnfv.org/gerrit/snaps ${REPOS_DIR}/snaps RUN git clone --depth 1 https://gerrit.opnfv.org/gerrit/securityscanning ${REPOS_DIR}/securityscanning RUN git clone --depth 1 https://gerrit.opnfv.org/gerrit/releng ${REPOS_DIR}/releng # OpenStack repositories RUN git clone --depth 1 -b $OPENSTACK_TAG https://github.com/openstack/networking-bgpvpn ${REPOS_DIR}/bgpvpn -#RUN git clone --depth 1 -b $KINGBIRD_TAG https://github.com/openstack/kingbird.git ${REPOS_DIR}/kingbird +RUN git clone --depth 1 -b $KINGBIRD_TAG https://github.com/openstack/kingbird.git ${REPOS_DIR}/kingbird RUN git clone --depth 1 -b $RALLY_TAG https://github.com/openstack/rally.git ${REPOS_DIR}/rally RUN git clone --depth 1 -b $TEMPEST_TAG https://github.com/openstack/tempest.git ${REPOS_DIR}/tempest @@ -117,11 +117,12 @@ RUN find ${FUNCTEST_REPO_DIR} -name "*.py" \ RUN /bin/bash ${REPOS_DIR}/parser/tests/parser_install.sh ${REPOS_DIR} RUN ${REPOS_DIR}/rally/install_rally.sh --yes +RUN /bin/bash ${REPOS_DIR}/functest/docker/add_images.sh + RUN gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 RUN curl -L https://get.rvm.io | bash -s stable # SNAPS integration -RUN git clone --depth 1 https://gerrit.cablelabs.com/snaps-provisioning ${REPOS_DIR}/snaps RUN pip install -e ${REPOS_DIR}/snaps/ # SFC integration @@ -132,8 +133,9 @@ RUN cd ${REPOS_DIR}/sfc && pip install . 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 + +# Kingbird integration +RUN cd ${REPOS_DIR}/kingbird && pip install -e . RUN /bin/bash -c ". /etc/profile.d/rvm.sh \ && cd ${REPOS_VNFS_DIR}/vims-test \ diff --git a/docker/add_images.sh b/docker/add_images.sh new file mode 100755 index 00000000..af2956c2 --- /dev/null +++ b/docker/add_images.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# +# This script downloads the images that are used for testing +# and places them in the functest docker image + +CIRROS_REPO_URL=http://download.cirros-cloud.net +CIRROS_AARCH64_TAG=161201 +CIRROS_X86_64_TAG=0.3.5 + +wget ${CIRROS_REPO_URL}/${CIRROS_X86_64_TAG}/cirros-${CIRROS_X86_64_TAG}-x86_64-disk.img -P ${FUNCTEST_BASE_DIR}/data/ +wget ${CIRROS_REPO_URL}/${CIRROS_X86_64_TAG}/cirros-${CIRROS_X86_64_TAG}-x86_64-lxc.tar.gz -P ${FUNCTEST_BASE_DIR}/data/ +wget http://205.177.226.237:9999/onosfw/firewall_block_image.img -P ${FUNCTEST_BASE_DIR}/data/ + +# Add the 3-part image for aarch64, since functest can be run from an x86 machine to test an aarch64 POD +wget ${CIRROS_REPO_URL}/daily/20${CIRROS_AARCH64_TAG}/cirros-d${CIRROS_AARCH64_TAG}-aarch64-disk.img -P ${FUNCTEST_BASE_DIR}/data/ +wget ${CIRROS_REPO_URL}/daily/20${CIRROS_AARCH64_TAG}/cirros-d${CIRROS_AARCH64_TAG}-aarch64-initramfs -P ${FUNCTEST_BASE_DIR}/data/ +wget ${CIRROS_REPO_URL}/daily/20${CIRROS_AARCH64_TAG}/cirros-d${CIRROS_AARCH64_TAG}-aarch64-kernel -P ${FUNCTEST_BASE_DIR}/data/ diff --git a/docs/configguide/configguide.rst b/docs/configguide/configguide.rst index c03760c5..08e089c2 100644 --- a/docs/configguide/configguide.rst +++ b/docs/configguide/configguide.rst @@ -331,7 +331,6 @@ should now be in place:: |-- domino |-- functest |-- kingbird - |-- moon |-- odl_test |-- onos |-- ovno diff --git a/docs/devguide/index.rst b/docs/devguide/index.rst index 21af912b..eee01367 100644 --- a/docs/devguide/index.rst +++ b/docs/devguide/index.rst @@ -104,7 +104,6 @@ The external test cases are: * onos * bgpvpn * copper - * moon * security_scan * sfc-odl * sfc-onos @@ -336,10 +335,6 @@ The API can described as follows. For detailed information, please go to Authentication: opnfv/api@opnfv -Please notes that POST/DELETE/PUT operations for test or study purpose via -swagger website is not allowed, because it will change the real data in -the database. - Version: +--------+--------------------------+-----------------------------------------+ @@ -504,17 +499,39 @@ Scenarios: The code of the API is hosted in the releng repository `[6]`_. +The static documentation of the API can be found at `[17]`_. The test API has been dockerized and may be installed locally in your lab. See `[15]`_ for details. The deployment of the test API has been automated. A jenkins job manages: * the unit tests of the test api - * the cration of a new docker file + * the creation of a new docker file * the deployment of the new test api * the archive of the old test api * the backup of the Mongo DB +Test API Authorization +~~~~~~~~~~~~~~~~~~~~~~ + +PUT/DELETE/POST operations of the testapi now require token based authorization. The token needs +to be added in the request using a header 'X-Auth-Token' for access to the database. + +e.g:: + headers['X-Auth-Token'] + +The value of the header i.e the token can be accessed in the jenkins environment variable +*TestApiToken*. The token value is added as a masked password. + +.. code-block:: python + + headers['X-Auth-Token'] = os.environ.get('TestApiToken') + +The above example is in Python. Token based authentication has been added so that only ci pods +jenkins job can have access to the database. + +Please note that currently token authorization is implemented but is not yet enabled. + Automatic reporting =================== @@ -556,8 +573,6 @@ A jenkins job manages: +---------------------+---------+---------+---------+---------+ | parser | | | X | | +---------------------+---------+---------+---------+---------+ - | moon | | X | | | - +---------------------+---------+---------+---------+---------+ | copper | X | | | X | +---------------------+---------+---------+---------+---------+ @@ -590,7 +605,7 @@ A jenkins job manages: stable) and then the number of iterations (4 needed) would not be sufficient to get the green status. - Please note that other test cases (e.g. sfc_odl, bgpvpn, moon) need also + Please note that other test cases (e.g. sfc_odl, bgpvpn) need also ODL configuration addons and as a consequence specific scenario. There are not considered as runnable on the generic odl_l2 scenario. @@ -963,6 +978,8 @@ _`[15]`: https://git.opnfv.org/cgit/releng/tree/utils/test/result_collection_api _`[16]`: https://git.opnfv.org/cgit/releng/tree/utils/test/scripts/mongo_to_elasticsearch.py +_`[17]`: http://artifacts.opnfv.org/releng/docs/testapi.html + OPNFV main site: http://www.opnfv.org OPNFV functional test page: https://wiki.opnfv.org/opnfv_functional_testing diff --git a/docs/internship/testapi_evolution/index.rst b/docs/internship/testapi_evolution/index.rst index f2583e2f..9cca9ebc 100644 --- a/docs/internship/testapi_evolution/index.rst +++ b/docs/internship/testapi_evolution/index.rst @@ -11,12 +11,17 @@ If not, see <http://creativecommons.org/licenses/by/4.0/>. Test API evolution ================== -Author: Rohit Sakala +Author: Sakala Venkata Krishna Rohit Mentors: S. Feng, J.Lausuch, M.Richomme Abstract ======== +The testapi is used by all the test opnfv projects to report results. +It is also used to declare projects, test cases and labs. A major refactoring +has been done in Colorado with the introduction of swagger. The testapi is defined in Functest +developer guide. The purpose of this project is to add more features to the testapi that automate +the tasks that are done manually now, though there are tasks other than automation. Version history =============== @@ -25,46 +30,208 @@ Version history | **Date** | **Ver.** | **Author** | **Comment** | | | | | | +------------+----------+------------------+------------------------+ -| 2016-??-?? | 0.0.1 | Morgan Richomme | Beginning of the | +| 2016-11-14 | 0.0.1 | Morgan Richomme | Beginning of the | | | | (Orange) | Internship | +------------+----------+------------------+------------------------+ - +| 2017-02-17 | 0.0.2 | S.V.K Rohit | End of the Internship | +| | | (IIIT Hyderabad) | | ++------------+----------+------------------+------------------------+ Overview: ========= - - +The internhip time period was from Nov 14th to Feb 17th. The project prosposal page is here `[1]`_. +The intern project was assigned to Svk Rohit and was mentored by S. Feng, J.Lausuch, M.Richomme. +The link to the patches submitted is `[2]`_. The internship was successfully completed and the +documentation is as follows. Problem Statement: ------------------ +The problem statement could be divided into pending features that needed to be added into testapi +repo. The following were to be accomplished within the internship time frame. + +* **Add verification jenkins job for the testapi code** + The purpose of this job is to verify whehter the unit tests are successful or not with the + inclusion of the patchset submitted. + +* **Automatic update of opnfv/testapi docker image** + The docker image of testapi is hosted in the opnfv docker hub. To ensure that the testapi image + is always updated with the repository, automatic updation of the image is necessary and a job + is triggered whenever a new patch gets merged. + +* **Automation deployment of testresults.opnfv.org/test/ website** + In the same manner as the docker image of testapi is updated, the testapi website needs to be + in sync with the repository code. So, a job has been added to the opnfv jenkins ci for the + updation of the testresults website. +* **Generate static documentation of testapi calls** + The purpose of this is to give an static/offline view of testapi. If someone wants to have a + look at the Restful apis of testapi, he/she does't need to go to the website, he can download + a html page and view it anytime. -Curation Phase --------------- +* **Backup MongoDB of testapi** + The mongoDB needs to be backed up every week. Till now it was done manually, but due to this + internship, it is now automated using a jenkins job. +* **Add token based authorization to the testapi calls** + The token based authorization was implemented to ensure that only ci_pods could access the + database. Authentication has been added to only delete/put/post requests. +Curation Phase: +--------------- +The curation phase was the first 3 to 4 weeks of the internship. This phase was to get familiar +with the testapi code and functionality and propose the solutions/tools for the tasks mentioned +above. Swagger codegen was choosen out of the four tools proposed `[3]`_ for generating static +documentaion. +Also, specific amount of time was spent on the script flow of the jenkins jobs. The automatic +deployment task involves accessing a remote server from inside the jenkins build. The deployment +had to be done only after the docker image update is done. For these constraints to satisfy, a +multijob jenkins job was choosen instead of a freestyle job. + +Important Links: +---------------- + +* MongoDB Backup Link - `[4]`_ +* Static Documentation - `[5]`_ +* TestAPI Token addition to ci_pods - `[6]`_ Schedule: ========= - +The progress and completion of the tasks is described in the below table. +--------------------------+------------------------------------------+ | **Date** | **Comment** | | | | +--------------------------+------------------------------------------+ -| December - January | ........ | +| Nov 14th - Dec 31st | Understand Testapi code and the | +| | requirements. | ++--------------------------+------------------------------------------+ +| Jan 1st - Jan 7th | Add jenkins job to create static | +| | documentation and write build scripts. | ++--------------------------+------------------------------------------+ +| Jan 8th - Jan 21st | Add verification jenkins job for unit | +| | tests. | ++--------------------------+------------------------------------------+ +| Jan 22nd - Jan 28th | Add jenkins job for mongodb backup | +| | | ++--------------------------+------------------------------------------+ +| Jan 29th - Feb 11th | Enable automatic deployment of | +| | testresults.opnfv.org/test/ | +--------------------------+------------------------------------------+ -| January - february | ........ | +| Feb 12th - Feb 17th | Add token based authentication | +| | | +--------------------------+------------------------------------------+ +FAQ's +===== + +This section lists the problems that I have faced and the understanding that I have acquired during +the internship. This section may help other developers in solving any errors casused because of the +code written as a part of this internship. + + +Test Api +-------- + +What is the difference between defining data_file as "/etc/.." and "etc/.." in setup.cfg ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If in the setup.cfg, it is defined as + +[files] +data_files = +etc/a.conf = etc/a.conf.sample + +then it ends up installed in the /usr/etc/. With this configuration, it would be installed +correctly within a venv. but when it is defined as + +[files] +data_files = +/etc/a.conf = etc/a.conf.sample + +then it ends up installed on the root of the filesystem instead of properly be installed within the +venv. + +Which attribute does swagger-codegen uses as the title in the generation of document generation ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It uses the nickname of the api call in swagger as the title in the generation of the document +generation. + +Does swagger-codegen take more than one yaml file as input ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No, swagger-codegen only takes one yaml file as input to its jar file. If there more than one yaml +file, one needs to merge them and give it as an input keeping mind the swagger specs. + + +Jenkins & JJB +------------- + +Which scm macro is used for verification jenkins jobs ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two macros for scm one is git-scm and other git-scm-gerrit. git-scm-gerrit is used for +verification jenkins job. + +Does the virtualenv created in one build script exists in other build scripts too ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No, the virtualenv created in one build script only exists in that build script/shell. + +What parameters are needed for the scm macros ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Project and Branch are the two parameters needed for scm macros. + +What is the directory inside the jenkins build ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The directory of the jenkins build is the directory of the repo. `ls $WORKSPACE` command will give +you all the contents of the directory. + +How to include a bash script in jenkins job yaml file ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An example might be apt here as an answer. + +builders: + - shell: + !include-raw: include-raw001-hello-world.sh + + +How do you make a build server run on a specific machine ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It can be done by defining a label parameter 'SLAVE_LABEL' or in OPNFV , there are macros for each +server, one can use those parameter macros. +Ex: opnfv-build-defaults. Note, if we use macro, then no need to define GIT_BASE, but if one uses +SLAVE_LABEL, one needs to define a parameter GIT_BASE. This is because macro already has GIT_BASE +defined. + +What job style should be used when there is a situation like one build should trigger other builds +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +or when different build scripts need to be run on different machines ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +MultiJob style should be used as it has phases where each phase can be taken as a build scipt and +can have its own parameters by which one can define the SLAVE_LABEL parameter. References: =========== -.. _`[1]` : https://wiki.opnfv.org/display/DEV/Intern+Project%3A+testapi+evolution +_`[1]` : https://wiki.opnfv.org/display/DEV/Intern+Project%3A+testapi+evolution + +_`[2]` : https://gerrit.opnfv.org/gerrit/#/q/status:merged+owner:%22Rohit+Sakala+%253Crohitsakala%2540gmail.com%253E%22 + +_`[3]` : https://docs.google.com/document/d/1jWwVZ1ZpKgKcOS_zSz2KzX1nwg4BXxzBxcwkesl7krw/edit?usp=sharing + +_`[4]` : http://artifacts.opnfv.org/testapibackup.html + +_`[5]` : http://artifacts.opnfv.org/releng/docs/testapi.html +_`[6]` : http://artifacts.opnfv.org/functest/review/26047/devguide/index.html#test-api-authorization diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index 7e821a84..9436de2b 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -372,7 +372,6 @@ Please refer to the dedicated feature user guides for details: * copper: http://artifacts.opnfv.org/copper/danube/docs/userguide/index.html * doctor: http://artifacts.opnfv.org/doctor/danube/userguide/index.html * domino: http://artifacts.opnfv.org/domino/docs/userguide-single/index.html - * moon: http://artifacts.opnfv.org/moon/docs/userguide/index.html * multisites: http://artifacts.opnfv.org/multisite/docs/userguide/index.html * onos-sfc: http://artifacts.opnfv.org/onosfw/danube/userguide/index.html * odl-sfc: http://artifacts.opnfv.org/sfc/danube/userguide/index.html diff --git a/docs/userguide/introduction.rst b/docs/userguide/introduction.rst index e5a090ed..4dfe7937 100644 --- a/docs/userguide/introduction.rst +++ b/docs/userguide/introduction.rst @@ -149,10 +149,6 @@ validate the scenario for the release. | | | multisites | Multisites | | | | | See `Multisite User Guide`_ for | | | | | details | -| | +----------------+----------------------------------+ -| | | moon | Security management system | -| | | | See `Moon User Guide`_ for | -| | | | details | +-------------+---------------+----------------+----------------------------------+ | VNF | vnf | cloudify_ims | Example of a real VNF deployment | | | | | to show the NFV capabilities of | @@ -257,4 +253,3 @@ section `Executing the functest suites`_ of this document. .. _`Functest Dashboard`: http://testresults.opnfv.org/kibana_dashboards/ .. _`SFC User Guide`: http://artifacts.opnfv.org/sfc/colorado/userguide/index.html .. _`Multisite User Guide`: http://artifacts.opnfv.org/multisite/docs/userguide/index.html -.. _`Moon User Guide`: http://artifacts.opnfv.org/moon/docs/userguide/index.html diff --git a/docs/userguide/runfunctest.rst b/docs/userguide/runfunctest.rst index ecf3a209..b5c7191c 100644 --- a/docs/userguide/runfunctest.rst +++ b/docs/userguide/runfunctest.rst @@ -287,7 +287,7 @@ variables: * The scenario [controller]-[feature]-[mode], stored in DEPLOY_SCENARIO with * controller = (odl|onos|ocl|nosdn) - * feature = (ovs(dpdk)|kvm|sfc|bgpvpn|moon|multisites) + * feature = (ovs(dpdk)|kvm|sfc|bgpvpn|multisites) * mode = (ha|noha) The constraints per test case are defined in the Functest configuration file diff --git a/functest/ci/check_os.sh b/functest/ci/check_os.sh index b875a173..2c5c021c 100755 --- a/functest/ci/check_os.sh +++ b/functest/ci/check_os.sh @@ -6,6 +6,16 @@ # jose.lausuch@ericsson.com # +declare -A service_cmd_array +service_cmd_array['nova']='openstack server list' +service_cmd_array['neutron']='openstack network list' +service_cmd_array['keystone']='openstack endpoint list' +service_cmd_array['cinder']='openstack volume list' +service_cmd_array['glance']='openstack image list' + +MANDATORY_SERVICES='nova neutron keystone glance' +OPTIONAL_SERVICES='cinder' + verify_connectivity() { for i in $(seq 0 9); do if echo "test" | nc -v -w 10 $1 $2 &>/dev/null; then @@ -16,6 +26,34 @@ verify_connectivity() { return 1 } +check_service() { + local service cmd + service=$1 + cmd=${service_cmd_array[$service]} + if [ -z "$2" ]; then + required='false' + else + required=$2 + fi + echo ">>Checking ${service} service..." + if ! openstack service list | grep -i ${service} > /dev/null; then + if [ "$required" == 'false' ]; then + echo "WARN: Optional Service ${service} is not enabled!" + return + else + echo "ERROR: Required Service ${service} is not enabled!" + exit 1 + fi + fi + $cmd &>/dev/null + result=$? + if [ $result -ne 0 ]; then + echo "ERROR: Failed execution $cmd. The $service does not seem to be working." + exit 1 + else + echo " ...OK" + fi +} if [ -z $OS_AUTH_URL ];then echo "ERROR: OS_AUTH_URL environment variable missing... Have you sourced the OpenStack credentials?" @@ -56,25 +94,16 @@ fi echo " ...OK" -echo "Checking OpenStack basic services:" -commands=('openstack endpoint list' 'openstack server list' 'openstack network list' \ - 'openstack image list' 'openstack volume list') -for cmd in "${commands[@]}" -do - service=$(echo $cmd | awk '{print $1, $2}') - echo ">>Checking $service service..." - $cmd &>/dev/null - result=$? - if [ $result -ne 0 ]; - then - echo "ERROR: Failed execution $cmd. The $service does not seem to be working." - exit 1 - else - echo " ...OK" - fi +echo "Checking Required OpenStack services:" +for service in $MANDATORY_SERVICES; do + check_service $service "true" done +echo "Required OpenStack services are OK." -echo "OpenStack services are OK." +echo "Checking Optional OpenStack services:" +for service in $OPTIONAL_SERVICES; do + check_service $service +done echo "Checking External network..." networks=($(neutron net-list -F id | tail -n +4 | head -n -1 | awk '{print $2}')) diff --git a/functest/ci/config_aarch64_patch.yaml b/functest/ci/config_aarch64_patch.yaml new file mode 100644 index 00000000..9a345e3f --- /dev/null +++ b/functest/ci/config_aarch64_patch.yaml @@ -0,0 +1,20 @@ +os: + general: + openstack: + image_name: TestVM + image_file_name: cirros-d161201-aarch64-disk.img + image_password: gocubsgo + + snaps_simple_healthcheck: + disk_image: /home/opnfv/functest/data/cirros-d161201-aarch64-disk.img + kernel_image: /home/opnfv/functest/data/cirros-d161201-aarch64-kernel + ramdisk_image: /home/opnfv/functest/data/cirros-d161201-aarch64-initramfs + extra_properties: + os_command_line: root=/dev/vdb1 rw rootwait console=tty0 console=ttyS0 console=ttyAMA0 + hw_video_model: vga + + vping: + image_name: TestVM + + doctor: + image_name: TestVM diff --git a/functest/ci/config_functest.yaml b/functest/ci/config_functest.yaml index f6cb14cb..489c395f 100755 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -5,7 +5,6 @@ general: 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 @@ -21,19 +20,23 @@ general: repo_sfc: /home/opnfv/repos/sfc dir_repo_onos: /home/opnfv/repos/onos repo_promise: /home/opnfv/repos/promise + repo_netready: /home/opnfv/repos/netready + repo_barometer: /home/opnfv/repos/barometer 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 + repo_securityscan: /home/opnfv/repos/securityscanning + repo_vrouter: /home/opnfv/repos/vrouter functest: /home/opnfv/functest functest_test: /home/opnfv/repos/functest/functest/opnfv_tests results: /home/opnfv/functest/results functest_logging_cfg: /home/opnfv/repos/functest/functest/ci/logging.json functest_conf: /home/opnfv/functest/conf functest_data: /home/opnfv/functest/data - dir_vIMS_data: /home/opnfv/functest/data/vIMS/ + ims_data: /home/opnfv/functest/data/ims/ rally_inst: /home/opnfv/.rally openstack: @@ -43,6 +46,8 @@ general: image_name: Cirros-0.3.4 image_file_name: cirros-0.3.4-x86_64-disk.img image_disk_format: qcow2 + image_username: cirros + image_password: cubswin:) flavor_name: opnfv_flavor flavor_ram: 512 @@ -125,48 +130,11 @@ vnf: cloudify_ims: tenant_name: cloudify_ims tenant_description: vIMS - tenant_images: - ubuntu_14.04: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img - centos_7: http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1510.qcow2 - 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' - requirements: - ram_min: 1700 - os_image: ubuntu_14.04 - inputs: - image_id: '' - flavor_id: '' - agent_user: 'ubuntu' - external_network_name: '' - public_domain: clearwater.opnfv + config: cloudify_ims.yaml orchestra_ims: tenant_name: orchestra_ims tenant_description: ims deployed with openbaton + config: orchestra_ims.yaml opera_ims: tenant_name: opera_ims tenant_description: ims deployed with open-o diff --git a/functest/ci/exec_test.sh b/functest/ci/exec_test.sh index 6a2b55a2..aa0cfaf7 100755 --- a/functest/ci/exec_test.sh +++ b/functest/ci/exec_test.sh @@ -105,14 +105,6 @@ function run_test(){ # no need to run anything until refactoring done # ${REPOS_DIR}/ovno/Testcases/RunTests.sh ;; - "security_scan") - echo "Sourcing Credentials ${FUNCTEST_CONF_DIR}/stackrc for undercloud .." - source ${FUNCTEST_CONF_DIR}/stackrc - python ${FUNCTEST_TEST_DIR}/security_scan/security_scan.py --config ${FUNCTEST_TEST_DIR}/security_scan/config.ini - ;; - "moon") - python ${REPOS_DIR}/moon/tests/run_tests.py $report - ;; *) echo "The test case '${test_name}' does not exist." exit 1 diff --git a/functest/ci/installer_params.yaml b/functest/ci/installer_params.yaml new file mode 100644 index 00000000..26aff9bb --- /dev/null +++ b/functest/ci/installer_params.yaml @@ -0,0 +1,16 @@ +apex: + ip: '' + user: 'stack' + pkey: '/root/.ssh/id_rsa' +# compass: +# ip: '192.168.200.2' +# user: 'root' +# password: 'root' +fuel: + ip: '10.20.0.2' + user: 'root' + password: 'r00tme' +# joid: +# ip: '' +# user: '' +# password: '' diff --git a/functest/ci/prepare_env.py b/functest/ci/prepare_env.py index b3e59020..f5bae6a0 100755 --- a/functest/ci/prepare_env.py +++ b/functest/ci/prepare_env.py @@ -1,53 +1,54 @@ #!/usr/bin/env python # -# Author: Jose Lausuch (jose.lausuch@ericsson.com) -# -# Installs the Functest framework within the Docker container -# and run the tests automatically -# -# # 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 argparse import json import os import re import subprocess import sys +import fileinput 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 from functest.utils.constants import CONST +from opnfv.utils import constants as opnfv_constants +from opnfv.deployment import factory + actions = ['start', 'check'] """ logging configuration """ logger = ft_logger.Logger("prepare_env").getLogger() - +handler = None +# set the architecture to default +pod_arch = None CONFIG_FUNCTEST_PATH = CONST.CONFIG_FUNCTEST_YAML CONFIG_PATCH_PATH = os.path.join(os.path.dirname( CONFIG_FUNCTEST_PATH), "config_patch.yaml") +CONFIG_AARCH64_PATCH_PATH = os.path.join(os.path.dirname( + CONFIG_FUNCTEST_PATH), "config_aarch64_patch.yaml") +RALLY_CONF_PATH = os.path.join("/etc/rally/rally.conf") +RALLY_AARCH64_PATCH_PATH = os.path.join(os.path.dirname( + CONFIG_FUNCTEST_PATH), "rally_aarch64_patch.conf") -with open(CONFIG_PATCH_PATH) as f: - functest_patch_yaml = yaml.safe_load(f) - -class PrepareEnvParser(): +class PrepareEnvParser(object): def __init__(self): self.parser = argparse.ArgumentParser() self.parser.add_argument("action", help="Possible actions are: " - "'{d[0]}|{d[1]}' ".format(d=actions)) + "'{d[0]}|{d[1]}' ".format(d=actions), + choices=actions) self.parser.add_argument("-d", "--debug", help="Debug mode", action="store_true") @@ -106,6 +107,34 @@ def check_env_variables(): logger.info(" IS_CI_RUN=%s" % CONST.IS_CI_RUN) +def get_deployment_handler(): + global handler + global pod_arch + + installer_params_yaml = os.path.join(CONST.dir_repo_functest, + 'functest/ci/installer_params.yaml') + if (CONST.INSTALLER_IP and CONST.INSTALLER_TYPE and + CONST.INSTALLER_TYPE in opnfv_constants.INSTALLERS): + installer_params = ft_utils.get_parameter_from_yaml( + CONST.INSTALLER_TYPE, installer_params_yaml) + + user = installer_params.get('user', None) + password = installer_params.get('password', None) + pkey = installer_params.get('pkey', None) + + try: + handler = factory.Factory.get_handler( + installer=CONST.INSTALLER_TYPE, + installer_ip=CONST.INSTALLER_IP, + installer_user=user, + installer_pwd=password, + pkey_file=pkey) + if handler: + pod_arch = handler.get_arch() + except Exception as e: + logger.debug("Cannot get deployment information. %s" % e) + + def create_directories(): print_separator() logger.info("Creating needed directories...") @@ -140,14 +169,14 @@ def source_rc_file(): 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.") + raise Exception("Missing CI_INSTALLER_IP.") 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." % (CONST.INSTALLER_TYPE, opnfv_constants.INSTALLERS)) - sys.exit("Wrong INSTALLER_TYPE.") + raise Exception("Wrong INSTALLER_TYPE.") cmd = ("/home/opnfv/repos/releng/utils/fetch_os_creds.sh " "-d %s -i %s -a %s" @@ -159,23 +188,18 @@ def source_rc_file(): output = p.communicate()[0] logger.debug("\n%s" % output) if p.returncode != 0: - logger.error("Failed to fetch credentials from installer.") - sys.exit(1) + raise Exception("Failed to fetch credentials from installer.") else: logger.info("RC file provided in %s." % CONST.openstack_creds) if os.path.getsize(CONST.openstack_creds) == 0: - logger.error("The file %s is empty." - % CONST.openstack_creds) - sys.exit(1) + raise Exception("The file %s is empty." % CONST.openstack_creds) logger.info("Sourcing the OpenStack RC file...") - creds = os_utils.source_credentials( + os_utils.source_credentials( CONST.openstack_creds) - str = "" - for key, value in creds.iteritems(): + for key, value in os.environ.iteritems(): if re.search("OS_", key): - str += "\n\t\t\t\t\t\t " + key + "=" + value if key == 'OS_AUTH_URL': CONST.OS_AUTH_URL = value elif key == 'OS_USERNAME': @@ -186,12 +210,18 @@ def source_rc_file(): CONST.OS_PASSWORD = value -def patch_config_file(): +def patch_config_file(patch_file_path, arch_filter=None): + if arch_filter and pod_arch not in arch_filter: + return + + with open(patch_file_path) as f: + patch_file = yaml.safe_load(f) + updated = False - for key in functest_patch_yaml: + for key in patch_file: if key in CONST.DEPLOY_SCENARIO: new_functest_yaml = dict(ft_utils.merge_dicts( - ft_utils.get_functest_yaml(), functest_patch_yaml[key])) + ft_utils.get_functest_yaml(), patch_file[key])) updated = True if updated: @@ -213,12 +243,23 @@ def verify_deployment(): line = p.stdout.readline().rstrip() if "ERROR" in line: logger.error(line) - sys.exit("Problem while running 'check_os.sh'.") + raise Exception("Problem while running 'check_os.sh'.") logger.info(line) def install_rally(): print_separator() + + if 'aarch64' in pod_arch: + logger.info("Apply aarch64 specific to rally config...") + with open(RALLY_AARCH64_PATCH_PATH, "r") as f: + rally_patch_conf = f.read() + + for line in fileinput.input(RALLY_CONF_PATH, inplace=1): + print line, + if "cirros|testvm" in line: + print rally_patch_conf + logger.info("Creating Rally environment...") cmd = "rally deployment destroy opnfv-rally" @@ -226,20 +267,19 @@ def install_rally(): "Deployment %s does not exist." % 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={}" + "--file=rally_conf.json --name={0}" .format(CONST.rally_deployment_name)) - ft_utils.execute_command(cmd, - error_msg=("Problem while creating " - "Rally deployment")) + error_msg = "Problem while creating Rally deployment" + ft_utils.execute_command_raise(cmd, error_msg=error_msg) cmd = "rally deployment check" - ft_utils.execute_command(cmd, - error_msg=("OpenStack not responding or " - "faulty Rally deployment.")) + error_msg = "OpenStack not responding or faulty Rally deployment." + ft_utils.execute_command_raise(cmd, error_msg=error_msg) cmd = "rally deployment list" ft_utils.execute_command(cmd, @@ -254,64 +294,81 @@ def install_rally(): def install_tempest(): logger.info("Installing tempest from existing repo...") - cmd = ("rally verify create-verifier --source {0} " - "--name {1} --type tempest" - .format(CONST.dir_repo_tempest, CONST.tempest_deployment_name)) - ft_utils.execute_command(cmd, - error_msg="Problem while installing Tempest.") + cmd = ("rally verify list-verifiers | " + "grep '{0}' | wc -l".format(CONST.tempest_deployment_name)) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) + while p.poll() is None: + line = p.stdout.readline().rstrip() + if str(line) == '0': + logger.debug("Tempest %s does not exist" % + CONST.tempest_deployment_name) + cmd = ("rally verify create-verifier --source {0} " + "--name {1} --type tempest" + .format(CONST.dir_repo_tempest, + CONST.tempest_deployment_name)) + error_msg = "Problem while installing Tempest." + ft_utils.execute_command_raise(cmd, error_msg=error_msg) def create_flavor(): - os_utils.get_or_create_flavor('m1.tiny', - '512', - '1', - '1', - public=True) + _, flavor_id = os_utils.get_or_create_flavor('m1.tiny', + '512', + '1', + '1', + public=True) + if flavor_id is None: + raise Exception('Failed to create flavor') def check_environment(): msg_not_active = "The Functest environment is not installed." if not os.path.isfile(CONST.env_active): - logger.error(msg_not_active) - sys.exit(1) + raise Exception(msg_not_active) with open(CONST.env_active, "r") as env_file: s = env_file.read() if not re.search("1", s): - logger.error(msg_not_active) - sys.exit(1) + raise Exception(msg_not_active) logger.info("Functest environment is installed.") -def main(**kwargs): - if not (kwargs['action'] in actions): - logger.error('Argument not valid.') - sys.exit() - - if kwargs['action'] == "start": - logger.info("######### Preparing Functest environment #########\n") - check_env_variables() - create_directories() - source_rc_file() - patch_config_file() - verify_deployment() - install_rally() - install_tempest() - create_flavor() +def print_deployment_info(): + if handler: + logger.info('\n\nDeployment information:\n%s' % + handler.get_deployment_info()) - with open(CONST.env_active, "w") as env_file: - env_file.write("1") - check_environment() - - if kwargs['action'] == "check": - check_environment() - - exit(0) +def main(**kwargs): + try: + if not (kwargs['action'] in actions): + logger.error('Argument not valid.') + return -1 + elif kwargs['action'] == "start": + logger.info("######### Preparing Functest environment #########\n") + check_env_variables() + get_deployment_handler() + create_directories() + source_rc_file() + patch_config_file(CONFIG_PATCH_PATH) + patch_config_file(CONFIG_AARCH64_PATCH_PATH, 'aarch64') + verify_deployment() + install_rally() + install_tempest() + create_flavor() + with open(CONST.env_active, "w") as env_file: + env_file.write("1") + check_environment() + print_deployment_info() + elif kwargs['action'] == "check": + check_environment() + except Exception as e: + logger.error(e) + return -1 + return 0 if __name__ == '__main__': parser = PrepareEnvParser() args = parser.parse_args(sys.argv[1:]) - main(**args) + sys.exit(main(**args)) diff --git a/functest/ci/rally_aarch64_patch.conf b/functest/ci/rally_aarch64_patch.conf new file mode 100644 index 00000000..a49588bf --- /dev/null +++ b/functest/ci/rally_aarch64_patch.conf @@ -0,0 +1,5 @@ +img_name_regex = ^TestVM$ +img_url = http://download.cirros-cloud.net/daily/20161201/cirros-d161201-aarch64-disk.img +flavor_ref_ram = 128 +flavor_ref_alt_ram = 256 +heat_instance_type_ram = 128 diff --git a/functest/ci/run_tests.py b/functest/ci/run_tests.py index 320102dd..f920e70d 100755 --- a/functest/ci/run_tests.py +++ b/functest/ci/run_tests.py @@ -10,6 +10,7 @@ import argparse import datetime +import enum import importlib import os import re @@ -35,10 +36,19 @@ logger = ft_logger.Logger("run_tests").getLogger() 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 +# this variable will change to Result.EX_ERROR -class RunTestsParser(): +class Result(enum.Enum): + EX_OK = os.EX_OK + EX_ERROR = -1 + + +class BlockingTestFailed(Exception): + pass + + +class RunTestsParser(object): def __init__(self): self.parser = argparse.ArgumentParser() @@ -60,7 +70,7 @@ class RunTestsParser(): class GlobalVariables: EXECUTED_TEST_CASES = [] - OVERALL_RESULT = 0 + OVERALL_RESULT = Result.EX_OK CLEAN_FLAG = True REPORT_FLAG = False @@ -75,11 +85,10 @@ def print_separator(str, count=45): def source_rc_file(): 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) + raise Exception("RC file %s does not exist..." % rc_file) logger.debug("Sourcing the OpenStack RC file...") - creds = os_utils.source_credentials(rc_file) - for key, value in creds.iteritems(): + os_utils.source_credentials(rc_file) + for key, value in os.environ.iteritems(): if re.search("OS_", key): if key == 'OS_AUTH_URL': ft_constants.OS_AUTH_URL = value @@ -155,7 +164,7 @@ def run_test(test, tier_name, testcases=None): result = test_case.run() if result == testcase_base.TestcaseBase.EX_OK: if GlobalVariables.REPORT_FLAG: - test_case.publish_report() + test_case.push_to_db() result = test_case.check_criteria() except ImportError: logger.exception("Cannot import module {}".format( @@ -179,19 +188,16 @@ def run_test(test, tier_name, testcases=None): if result != 0: logger.error("The test case '%s' failed. " % test_name) - GlobalVariables.OVERALL_RESULT = -1 + GlobalVariables.OVERALL_RESULT = Result.EX_ERROR result_str = "FAIL" if test.is_blocking(): if not testcases or testcases == "all": - logger.info("This test case is blocking. Aborting overall " - "execution.") # if it is a single test we don't print the whole results table update_test_info(test_name, result_str, duration_str) generate_report.main(GlobalVariables.EXECUTED_TEST_CASES) - logger.info("Execution exit value: %s" % - GlobalVariables.OVERALL_RESULT) - sys.exit(GlobalVariables.OVERALL_RESULT) + raise BlockingTestFailed("The test case {} failed and is blocking" + .format(test.get_name())) update_test_info(test_name, result_str, duration_str) @@ -246,33 +252,37 @@ def main(**kwargs): if kwargs['report']: GlobalVariables.REPORT_FLAG = True - if kwargs['test']: - source_rc_file() - if _tiers.get_tier(kwargs['test']): - run_tier(_tiers.get_tier(kwargs['test'])) - - elif _tiers.get_test(kwargs['test']): - run_test(_tiers.get_test(kwargs['test']), - _tiers.get_tier(kwargs['test']), - kwargs['test']) - - elif kwargs['test'] == "all": - run_all(_tiers) - + try: + if kwargs['test']: + source_rc_file() + if _tiers.get_tier(kwargs['test']): + GlobalVariables.EXECUTED_TEST_CASES = generate_report.init( + [_tiers.get_tier(kwargs['test'])]) + run_tier(_tiers.get_tier(kwargs['test'])) + elif _tiers.get_test(kwargs['test']): + run_test(_tiers.get_test(kwargs['test']), + _tiers.get_tier(kwargs['test']), + kwargs['test']) + elif kwargs['test'] == "all": + run_all(_tiers) + else: + logger.error("Unknown test case or tier '%s', " + "or not supported by " + "the given scenario '%s'." + % (kwargs['test'], CI_SCENARIO)) + logger.debug("Available tiers are:\n\n%s" + % _tiers) + return Result.EX_ERROR else: - logger.error("Unknown test case or tier '%s', or not supported by " - "the given scenario '%s'." - % (kwargs['test'], CI_SCENARIO)) - logger.debug("Available tiers are:\n\n%s" - % _tiers) - else: - run_all(_tiers) - + run_all(_tiers) + except Exception as e: + logger.error(e) + GlobalVariables.OVERALL_RESULT = Result.EX_ERROR logger.info("Execution exit value: %s" % GlobalVariables.OVERALL_RESULT) - sys.exit(GlobalVariables.OVERALL_RESULT) + return GlobalVariables.OVERALL_RESULT if __name__ == '__main__': parser = RunTestsParser() args = parser.parse_args(sys.argv[1:]) - main(**args) + sys.exit(main(**args).value) diff --git a/functest/ci/testcases.yaml b/functest/ci/testcases.yaml index 6397f764..ed3a0b84 100755 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -8,16 +8,20 @@ tiers: operations in the VIM. testcases: - - name: healthcheck + name: snaps_health_check criteria: 'status == "PASS"' blocking: true description: >- - This test case verifies the basic OpenStack services like - Keystone, Glance, Cinder, Neutron and Nova. - + This test case creates executes the SimpleHealthCheck + Python test class which creates an, image, flavor, network, + and Cirros VM instance and observes the console output to + validate the single port obtains the correct IP address. dependencies: installer: '' scenario: '^((?!lxd).)*$' + run: + module: 'functest.opnfv_tests.openstack.snaps.health_check' + class: 'HealthCheck' - name: connection_check criteria: 'status == "PASS"' @@ -124,9 +128,9 @@ tiers: criteria: 'success_rate == 100%' blocking: true description: >- - Test Suite for the OpenDaylight SDN Controller. It integrates - some test suites from upstream using Robot as the test - framework. + Test Suite for the OpenDaylight SDN Controller. It + integrates some test suites from upstream using + Robot as the test framework. dependencies: installer: '' scenario: 'odl' @@ -139,6 +143,27 @@ tiers: - /home/opnfv/repos/odl_test/csit/suites/openstack/neutron - + name: odl_netvirt + criteria: 'success_rate == 100%' + blocking: true + description: >- + Test Suite for the OpenDaylight SDN Controller when + the NetVirt features are installed. It integrates + some test suites from upstream using Robot as the + test framework. + dependencies: + installer: '' + scenario: 'netvirt' + run: + module: 'functest.opnfv_tests.sdn.odl.odl' + class: 'ODLTests' + args: + suites: + - /home/opnfv/repos/odl_test/csit/suites/integration/basic + - /home/opnfv/repos/odl_test/csit/suites/openstack/neutron + - /home/opnfv/repos/odl_test/csit/suites/openstack/connectivity + + - name: onos criteria: 'status == "PASS"' blocking: true @@ -223,10 +248,13 @@ tiers: criteria: 'status == "PASS"' blocking: false description: >- - Simple security Scan + Simple Security Scan dependencies: installer: 'apex' scenario: '^((?!fdio).)*$' + run: + module: 'functest.opnfv_tests.features.security_scan' + class: 'SecurityScan' # - # name: copper # criteria: 'status == "PASS"' @@ -240,15 +268,6 @@ tiers: # module: 'functest.opnfv_tests.features.copper' # class: 'Copper' - - name: moon - criteria: 'status == "PASS"' - blocking: false - description: >- - Security management system for OPNFV - dependencies: - installer: 'compass' - scenario: '(odl)*(moon)' - - name: multisite criteria: 'success_rate == 100%' blocking: false @@ -294,6 +313,18 @@ tiers: module: 'functest.opnfv_tests.vnf.rnc.parser' class: 'Parser' - + name: domino + criteria: 'status == "PASS"' + blocking: false + description: >- + Test suite from Domino project. + dependencies: + installer: '' + scenario: '' + run: + module: 'functest.opnfv_tests.features.domino' + class: 'Domino' + - name: orchestra criteria: 'ret == 0' blocking: false @@ -305,41 +336,96 @@ tiers: run: module: 'functest.opnfv_tests.features.orchestrator.orchestra' class: 'OpenbatonOrchestrator' + - + name: netready + criteria: 'status == "PASS"' + blocking: false + description: >- + Test suite from Netready project. + dependencies: + installer: 'apex' + scenario: 'gluon' + run: + module: 'functest.opnfv_tests.features.netready' + class: 'GluonVping' + - + name: barometer + criteria: 'status == "PASS"' + blocking: false + description: >- + Test suite for the Barometer project. Separate tests verify the + proper configuration and functionality of the following + collectd plugins Ceilometer, Hugepages, Memory RAS (mcelog), + and OVS Events + dependencies: + installer: 'fuel' + scenario: 'kvm_ovs_dpdk_bar' + run: + module: 'functest.opnfv_tests.features.barometer' + class: 'BarometerCollectd' - name: components order: 3 - ci_loop: 'weekly' + ci_loop: 'daily' description : >- Extensive testing of OpenStack API. testcases: +# - +# name: tempest_full_parallel +# criteria: 'success_rate >= 80%' +# blocking: false +# description: >- +# The list of test cases is generated by +# Tempest automatically and depends on the parameters of +# the OpenStack deplopyment. +# dependencies: +# installer: '^((?!netvirt).)*$' +# scenario: '' +# run: +# module: 'functest.opnfv_tests.openstack.tempest.tempest' +# class: 'TempestFullParallel' - - name: tempest_full_parallel - criteria: 'success_rate >= 80%' + name: tempest_defcore + criteria: 'success_rate == 100%' blocking: false description: >- - The list of test cases is generated by - Tempest automatically and depends on the parameters of - the OpenStack deplopyment. + This is the set of Tempest test cases created by OpenStack + Interop Working Group for certification purposes. dependencies: - installer: '^((?!netvirt).)*$' - scenario: '' + installer: '' + scenario: 'nosdn-nofeature-ha' run: module: 'functest.opnfv_tests.openstack.tempest.tempest' - class: 'TempestFullParallel' - + class: 'TempestDefcore' - - name: rally_full - criteria: 'success_rate >= 90%' + name: tempest_custom + criteria: 'success_rate == 100%' blocking: false description: >- - This test case runs the full suite of scenarios of the OpenStack - Rally suite using several threads and iterations. + The test case allows running a customized list of tempest + test cases defined in a file under + <dir_functest_repo>/functest/opnfv_tests/openstack/ + /tempest/custom_tests/test_list.txt + The file is empty and can be customized with the desired tests. dependencies: - installer: '^((?!netvirt).)*$' - scenario: '' + installer: 'unknown' + scenario: 'unknown' run: - module: 'functest.opnfv_tests.openstack.rally.rally' - class: 'RallyFull' + module: 'functest.opnfv_tests.openstack.tempest.tempest' + class: 'TempestCustom' +# - +# name: rally_full +# criteria: 'success_rate >= 90%' +# blocking: false +# description: >- +# This test case runs the full suite of scenarios of the OpenStack +# Rally suite using several threads and iterations. +# dependencies: +# installer: '^((?!netvirt).)*$' +# scenario: '' +# run: +# module: 'functest.opnfv_tests.openstack.rally.rally' +# class: 'RallyFull' - name: vnf @@ -394,8 +480,8 @@ tiers: description: >- VNF deployment with OpenBaton (Orchestra) dependencies: - installer: 'unknown' - scenario: 'unknown' + installer: '' + scenario: '' run: module: 'functest.opnfv_tests.vnf.ims.orchestra_ims' class: 'ImsVnf' @@ -412,3 +498,16 @@ tiers: run: module: 'functest.opnfv_tests.vnf.ims.opera_ims' class: 'ImsVnf' + + - + name: vyos_vrouter + criteria: 'status == "PASS"' + blocking: false + description: >- + This test case is vRouter testing. + dependencies: + installer: 'fuel' + scenario: 'nosdn-nofeature' + run: + module: 'functest.opnfv_tests.vnf.router.vyos_vrouter' + class: 'VrouterVnf' diff --git a/functest/ci/tier_builder.py b/functest/ci/tier_builder.py index e1c3e49e..dae7c73e 100755 --- a/functest/ci/tier_builder.py +++ b/functest/ci/tier_builder.py @@ -11,7 +11,7 @@ import tier_handler as th import yaml -class TierBuilder: +class TierBuilder(object): def __init__(self, ci_installer, ci_scenario, testcases_file): self.ci_installer = ci_installer diff --git a/functest/ci/tier_handler.py b/functest/ci/tier_handler.py index 1eadfba5..127986bf 100755 --- a/functest/ci/tier_handler.py +++ b/functest/ci/tier_handler.py @@ -28,7 +28,7 @@ def split_text(text, max_len): return lines -class Tier: +class Tier(object): def __init__(self, name, order, ci_loop, description=""): self.tests_array = [] @@ -102,7 +102,7 @@ class Tier: return out -class TestCase: +class TestCase(object): def __init__(self, name, dependency, criteria, blocking, description=""): self.name = name @@ -160,7 +160,7 @@ class TestCase: return out -class Dependency: +class Dependency(object): def __init__(self, installer, scenario): self.installer = installer diff --git a/functest/cli/commands/cli_env.py b/functest/cli/commands/cli_env.py index 9423631b..14ad01bf 100644 --- a/functest/cli/commands/cli_env.py +++ b/functest/cli/commands/cli_env.py @@ -16,7 +16,7 @@ from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils -class CliEnv: +class CliEnv(object): def __init__(self): pass diff --git a/functest/cli/commands/cli_os.py b/functest/cli/commands/cli_os.py index aeb34974..f85f4041 100644 --- a/functest/cli/commands/cli_os.py +++ b/functest/cli/commands/cli_os.py @@ -18,7 +18,7 @@ import functest.utils.openstack_clean as os_clean import functest.utils.openstack_snapshot as os_snapshot -class CliOpenStack: +class CliOpenStack(object): def __init__(self): self.os_auth_url = CONST.OS_AUTH_URL diff --git a/functest/cli/commands/cli_testcase.py b/functest/cli/commands/cli_testcase.py index b6566245..6644a0c2 100644 --- a/functest/cli/commands/cli_testcase.py +++ b/functest/cli/commands/cli_testcase.py @@ -19,7 +19,7 @@ import functest.utils.functest_utils as ft_utils import functest.utils.functest_vacation as vacation -class CliTestcase: +class CliTestcase(object): def __init__(self): self.tiers = tb.TierBuilder(CONST.INSTALLER_TYPE, diff --git a/functest/cli/commands/cli_tier.py b/functest/cli/commands/cli_tier.py index b9d25b6d..012b11d0 100644 --- a/functest/cli/commands/cli_tier.py +++ b/functest/cli/commands/cli_tier.py @@ -18,7 +18,7 @@ from functest.utils.constants import CONST import functest.utils.functest_utils as ft_utils -class CliTier: +class CliTier(object): def __init__(self): self.tiers = tb.TierBuilder(CONST.INSTALLER_TYPE, diff --git a/functest/core/feature_base.py b/functest/core/feature_base.py index 873e21da..2bd1ec83 100644 --- a/functest/core/feature_base.py +++ b/functest/core/feature_base.py @@ -7,6 +7,7 @@ from functest.utils.constants import CONST class FeatureBase(base.TestcaseBase): + def __init__(self, project='functest', case='', repo='', cmd=''): super(FeatureBase, self).__init__() self.project_name = project @@ -19,13 +20,21 @@ class FeatureBase(base.TestcaseBase): def run(self, **kwargs): self.prepare() self.start_time = time.time() - ret = ft_utils.execute_command(self.cmd, output_file=self.result_file) + ret = self.execute() self.stop_time = time.time() self.post() self.parse_results(ret) self.log_results() + self.logger.info("Test result is stored in '%s'" % self.result_file) return base.TestcaseBase.EX_OK + def execute(self): + ''' + Executer method that can be overwritten + By default it executes a shell command. + ''' + return ft_utils.execute_command(self.cmd, output_file=self.result_file) + def prepare(self, **kwargs): pass diff --git a/functest/core/testcase_base.py b/functest/core/testcase_base.py index ec46bc64..838b6398 100644 --- a/functest/core/testcase_base.py +++ b/functest/core/testcase_base.py @@ -9,7 +9,6 @@ import os -from functest.utils.constants import CONST import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils @@ -18,7 +17,7 @@ class TestcaseBase(object): EX_OK = os.EX_OK EX_RUN_ERROR = os.EX_SOFTWARE - EX_PUBLISH_RESULT_FAILED = os.EX_SOFTWARE - 1 + EX_PUSH_TO_DB_ERROR = os.EX_SOFTWARE - 1 EX_TESTCASE_FAILED = os.EX_SOFTWARE - 2 logger = ft_logger.Logger(__name__).getLogger() @@ -44,45 +43,21 @@ class TestcaseBase(object): self.logger.error("Run must be implemented") return TestcaseBase.EX_RUN_ERROR - def publish_report(self): - if "RESULTS_STORE" in os.environ: - CONST.results_test_db_url = os.environ['RESULTS_STORE'] - + def push_to_db(self): try: assert self.project_name assert self.case_name assert self.criteria assert self.start_time assert self.stop_time - if CONST.results_test_db_url.lower().startswith( - ("http://", "https://")): - self.push_to_db() - elif CONST.results_test_db_url.lower().startswith("file://"): - self.write_to_file() + if ft_utils.push_results_to_db( + self.project_name, self.case_name, self.start_time, + self.stop_time, self.criteria, self.details): + self.logger.info("The results were successfully pushed to DB") + return TestcaseBase.EX_OK else: - self.logger.error("Please check parameter test_db_url and " - "OS environ variable RESTULTS_STORE") - return TestcaseBase.EX_PUBLISH_RESULT_FAILED + self.logger.error("The results cannot be pushed to DB") + return TestcaseBase.EX_PUSH_TO_DB_ERROR except Exception: - self.logger.exception("The results cannot be stored") - return TestcaseBase.EX_PUBLISH_RESULT_FAILED - - def write_to_file(self): - if ft_utils.write_results_to_file( - self.project_name, self.case_name, self.start_time, - self.stop_time, self.criteria, self.details): - self.logger.info("The results were successfully written to a file") - return TestcaseBase.EX_OK - else: - self.logger.error("write results to a file failed") - return TestcaseBase.EX_PUBLISH_RESULT_FAILED - - def push_to_db(self): - if ft_utils.push_results_to_db( - self.project_name, self.case_name, self.start_time, - self.stop_time, self.criteria, self.details): - self.logger.info("The results were successfully pushed to DB") - return TestcaseBase.EX_OK - else: - self.logger.error("The results cannot be pushed to DB") - return TestcaseBase.EX_PUBLISH_RESULT_FAILED + self.logger.exception("The results cannot be pushed to DB") + return TestcaseBase.EX_PUSH_TO_DB_ERROR diff --git a/functest/core/vnf_base.py b/functest/core/vnf_base.py index 4d019858..9438dca1 100644 --- a/functest/core/vnf_base.py +++ b/functest/core/vnf_base.py @@ -7,12 +7,10 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 -import os import time import inspect - import functest.utils.functest_logger as ft_logger import functest.utils.openstack_utils as os_utils import functest.utils.functest_utils as ft_utils @@ -69,8 +67,8 @@ class VnfOnBoardingBase(base.TestcaseBase): res_orchestrator['result']) self.details['orchestrator']['duration'] = round( orchestrator_ready_time - self.start_time, 1) - except: - self.logger.warn("Problem with the Orchestrator") + except Exception: + self.logger.warn("Problem with the Orchestrator", exc_info=True) # Deploy VNF try: @@ -113,9 +111,9 @@ class VnfOnBoardingBase(base.TestcaseBase): self.keystone_client = os_utils.get_keystone_client() self.logger.info("Prepare OpenStack plateform(create tenant and user)") - user_id = os_utils.get_user_id(self.keystone_client, - self.creds['username']) - if user_id == '': + admin_user_id = os_utils.get_user_id(self.keystone_client, + self.creds['username']) + if admin_user_id == '': self.step_failure("Failed to get id of " + self.creds['username']) @@ -135,7 +133,7 @@ class VnfOnBoardingBase(base.TestcaseBase): self.logger.error("Failed to get id for %s role" % role_name) self.step_failure("Failed to get role id of " + role_name) - if not os_utils.add_role_user(self.keystone_client, user_id, + if not os_utils.add_role_user(self.keystone_client, admin_user_id, role_id, tenant_id): self.logger.error("Failed to add %s on tenant" % self.creds['username']) @@ -151,40 +149,25 @@ class VnfOnBoardingBase(base.TestcaseBase): self.logger.error("Failed to create %s user" % self.tenant_name) self.step_failure("Failed to create user ") + if not os_utils.add_role_user(self.keystone_client, user_id, + role_id, tenant_id): + self.logger.error("Failed to add %s on tenant" % + self.tenant_name) + self.step_failure("Failed to add %s on tenant" % + self.tenant_name) + self.logger.info("Update OpenStack creds informations") - self.creds.update({ - "tenant": self.tenant_name, + self.admin_creds = self.creds.copy() + self.admin_creds.update({ + "tenant": self.tenant_name }) - self.neutron_client = os_utils.get_neutron_client(self.creds) - self.nova_client = os_utils.get_nova_client(self.creds) + self.neutron_client = os_utils.get_neutron_client(self.admin_creds) + self.nova_client = os_utils.get_nova_client(self.admin_creds) self.creds.update({ + "tenant": self.tenant_name, "username": self.tenant_name, "password": self.tenant_name, }) - self.glance_client = os_utils.get_glance_client(self.creds) - self.logger.info("Upload some OS images if it doesn't exist") - - temp_dir = os.path.join(self.data_dir, "tmp/") - for image_name, image_url in self.images.iteritems(): - image_id = os_utils.get_image_id(self.glance_client, image_name) - - if image_id == '': - self.logger.info("""%s image doesn't exist on glance repository. Try - downloading this image and upload on glance !""" % image_name) - image_id = os_utils.download_and_add_image_on_glance( - self.glance_client, image_name, image_url, temp_dir) - - if image_id == '': - self.step_failure( - "Failed to find or upload required OS " - "image for this deployment") - - self.logger.info("Update security group quota for this tenant") - - if not os_utils.update_sg_quota(self.neutron_client, - tenant_id, 50, 100): - self.step_failure("Failed to update security group quota" + - " for tenant " + self.tenant_name) # orchestrator is not mandatory to dpeloy and test VNF def deploy_orchestrator(self, **kwargs): diff --git a/functest/opnfv_tests/features/barometer.py b/functest/opnfv_tests/features/barometer.py new file mode 100644 index 00000000..aec2bce5 --- /dev/null +++ b/functest/opnfv_tests/features/barometer.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 + + +import functest.core.feature_base as base +import functest.utils.functest_logger as ft_logger + +from baro_tests import collectd + + +class BarometerCollectd(base.FeatureBase): + ''' + Class for executing barometercollectd testcase. + ''' + + def __init__(self): + super(BarometerCollectd, self).__init__(project='barometer', + case='barometercollectd', + repo='dir_repo_barometer') + self.logger = ft_logger.Logger("BarometerCollectd").getLogger() + + def execute(self): + return collectd.main(self.logger) diff --git a/functest/opnfv_tests/features/multisite.py b/functest/opnfv_tests/features/multisite.py deleted file mode 100755 index 15cfe2a4..00000000 --- a/functest/opnfv_tests/features/multisite.py +++ /dev/null @@ -1,22 +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 functest.utils.functest_logger as ft_logger - -logger = ft_logger.Logger("multisite").getLogger() - - -def main(): - logger.info("multisite OK") - - -if __name__ == '__main__': - main() diff --git a/functest/opnfv_tests/features/netready.py b/functest/opnfv_tests/features/netready.py new file mode 100644 index 00000000..dec2a23c --- /dev/null +++ b/functest/opnfv_tests/features/netready.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +# +import functest.core.feature_base as base + + +class GluonVping(base.FeatureBase): + + def __init__(self): + super(GluonVping, self).__init__(project='netready', + case='gluon_vping', + repo='dir_repo_netready') + dir_netready_functest = '{}/test/functest'.format(self.repo) + self.cmd = ('cd %s && python ./gluon-test-suite.py' % + dir_netready_functest) diff --git a/functest/opnfv_tests/features/security_scan.py b/functest/opnfv_tests/features/security_scan.py new file mode 100755 index 00000000..2db44175 --- /dev/null +++ b/functest/opnfv_tests/features/security_scan.py @@ -0,0 +1,24 @@ +#!/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 functest.core.feature_base as base +from functest.utils.constants import CONST + + +class SecurityScan(base.FeatureBase): + def __init__(self): + super(SecurityScan, self).__init__(project='securityscanning', + case='security_scan', + repo='dir_repo_securityscan') + self.cmd = ('bash {0} && ' + 'cd {1} && ' + 'python security_scan.py --config config.ini && ' + 'cd -'.format(CONST.openstack_creds, + self.repo)) diff --git a/functest/opnfv_tests/openstack/rally/rally.py b/functest/opnfv_tests/openstack/rally/rally.py index 16a872fc..46d6a570 100644 --- a/functest/opnfv_tests/openstack/rally/rally.py +++ b/functest/opnfv_tests/openstack/rally/rally.py @@ -526,14 +526,13 @@ class RallyBase(testcase_base.TestcaseBase): self._run_tests() self._generate_report() self._clean_up() + res = testcase_base.TestcaseBase.EX_OK except Exception as e: logger.error('Error with run: %s' % e) - return testcase_base.TestcaseBase.EX_RUN_ERROR - self.stop_time = time.time() + res = testcase_base.TestcaseBase.EX_RUN_ERROR - # If we are here, it means that the test case was successfully executed - # criteria is managed by the criteria Field - return testcase_base.TestcaseBase.EX_OK + self.stop_time = time.time() + return res class RallySanity(RallyBase): diff --git a/functest/opnfv_tests/openstack/snaps/health_check.py b/functest/opnfv_tests/openstack/snaps/health_check.py new file mode 100644 index 00000000..993c1000 --- /dev/null +++ b/functest/opnfv_tests/openstack/snaps/health_check.py @@ -0,0 +1,34 @@ +# 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 unittest + +from snaps.openstack.tests.os_source_file_test import OSIntegrationTestCase +from snaps.openstack.tests.create_instance_tests import SimpleHealthCheck + +from functest.core.pytest_suite_runner import PyTestSuiteRunner +from functest.opnfv_tests.openstack.snaps import snaps_utils +from functest.utils.constants import CONST + + +class HealthCheck(PyTestSuiteRunner): + """ + This test executes the SNAPS Python Test case SimpleHealthCheck which + creates a VM with a single port with an IPv4 address that is assigned by + DHCP. This test then validates the expected IP with the actual + """ + def __init__(self): + super(HealthCheck, self).__init__() + + self.suite = unittest.TestSuite() + self.case_name = "snaps_health_check" + ext_net_name = snaps_utils.get_ext_net_name() + + self.suite.addTest( + OSIntegrationTestCase.parameterize( + SimpleHealthCheck, CONST.openstack_creds, ext_net_name, + use_keystone=CONST.snaps_use_keystone)) diff --git a/functest/opnfv_tests/openstack/tempest/conf_utils.py b/functest/opnfv_tests/openstack/tempest/conf_utils.py index 91a5bb4b..893fff8c 100644 --- a/functest/opnfv_tests/openstack/tempest/conf_utils.py +++ b/functest/opnfv_tests/openstack/tempest/conf_utils.py @@ -13,8 +13,6 @@ import re import shutil import subprocess -import opnfv.utils.constants as releng_constants - from functest.utils.constants import CONST import functest.utils.functest_logger as ft_logger import functest.utils.functest_utils as ft_utils @@ -108,26 +106,35 @@ def get_verifier_deployment_dir(verifier_id, deployment_id): 'for-deployment-{}'.format(deployment_id)) -def configure_tempest(deployment_dir, IMAGE_ID=None, FLAVOR_ID=None): +def backup_tempest_config(conf_file): """ - Add/update needed parameters into tempest.conf file generated by Rally + Copy config file to tempest results directory """ - tempest_conf_file = os.path.join(deployment_dir, "tempest.conf") - if os.path.isfile(tempest_conf_file): - logger.debug("Verifier is already configured.") - logger.debug("Reconfiguring the current verifier...") - cmd = "rally verify configure-verifier --reconfigure" - else: - logger.info("Configuring the verifier...") - cmd = "rally verify configure-verifier" - ft_utils.execute_command(cmd) + if not os.path.exists(TEMPEST_RESULTS_DIR): + os.makedirs(TEMPEST_RESULTS_DIR) + + shutil.copyfile(conf_file, + os.path.join(TEMPEST_RESULTS_DIR, 'tempest.conf')) - logger.debug("Looking for 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 +def configure_tempest(deployment_dir, IMAGE_ID=None, FLAVOR_ID=None, + MODE=None): + """ + Calls rally verify and updates the generated tempest.conf with + given parameters + """ + conf_file = configure_verifier(deployment_dir) + configure_tempest_update_params(conf_file, + IMAGE_ID, FLAVOR_ID) + if MODE == 'feature_multisite': + configure_tempest_multisite_params(conf_file) + + +def configure_tempest_update_params(tempest_conf_file, + IMAGE_ID=None, FLAVOR_ID=None): + """ + Add/update needed parameters into tempest.conf file + """ logger.debug("Updating selected tempest.conf parameters...") config = ConfigParser.RawConfigParser() config.read(tempest_conf_file) @@ -171,36 +178,38 @@ def configure_tempest(deployment_dir, IMAGE_ID=None, FLAVOR_ID=None): with open(tempest_conf_file, 'wb') as config_file: config.write(config_file) - # Copy tempest.conf to /home/opnfv/functest/results/tempest/ - if not os.path.exists(TEMPEST_RESULTS_DIR): - os.makedirs(TEMPEST_RESULTS_DIR) - - shutil.copyfile(tempest_conf_file, - os.path.join(TEMPEST_RESULTS_DIR, 'tempest.conf')) - - return releng_constants.EXIT_OK + backup_tempest_config(tempest_conf_file) -def configure_tempest_multisite(deployment_dir): +def configure_verifier(deployment_dir): """ - Add/update needed parameters into tempest.conf file generated by Rally + Execute rally verify configure-verifier, which generates tempest.conf """ - logger.debug("configure the tempest") - configure_tempest(deployment_dir) + tempest_conf_file = os.path.join(deployment_dir, "tempest.conf") + if os.path.isfile(tempest_conf_file): + logger.debug("Verifier is already configured.") + logger.debug("Reconfiguring the current verifier...") + cmd = "rally verify configure-verifier --reconfigure" + else: + logger.info("Configuring the verifier...") + cmd = "rally verify configure-verifier" + ft_utils.execute_command(cmd) - 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.debug("Looking for tempest.conf file...") + if not os.path.isfile(tempest_conf_file): logger.error("Tempest configuration file %s NOT found." - % tempest_conf_old) - return releng_constants.EXIT_RUN_ERROR + % tempest_conf_file) + raise Exception("Tempest configuration file %s NOT found." + % tempest_conf_file) + else: + return tempest_conf_file - # 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...") +def configure_tempest_multisite_params(tempest_conf_file): + """ + Add/update multisite parameters into tempest.conf file generated by Rally + """ + logger.debug("Updating multisite tempest.conf parameters...") config = ConfigParser.RawConfigParser() config.read(tempest_conf_file) @@ -266,4 +275,4 @@ def configure_tempest_multisite(deployment_dir): with open(tempest_conf_file, 'wb') as config_file: config.write(config_file) - return releng_constants.EXIT_OK + backup_tempest_config(tempest_conf_file) diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/defcore_req.txt b/functest/opnfv_tests/openstack/tempest/custom_tests/defcore_req.txt index bb1d172d..1456db87 100644 --- a/functest/opnfv_tests/openstack/tempest/custom_tests/defcore_req.txt +++ b/functest/opnfv_tests/openstack/tempest/custom_tests/defcore_req.txt @@ -1,35 +1,19 @@ -# Set of DefCore tempest test cases (see http://www.openstack.org/brand/interop) -# This approved version (2016.01) is valid for Juno, Kilo, and Liberty releases of OpenStack -# The list is stored at http://git.openstack.org/cgit/openstack/defcore/plain/2016.01/2016.01.required.txt -tempest.api.compute.images.test_images.ImagesTestJSON.test_delete_saving_image[id-aa06b52b-2db5-4807-b218-9441f75d74e3] +# Set of DefCore tempest test cases not flagged and required. It only contains OpenStack core (no object storage) +# The approved guidelines (2016.08) are valid for Kilo, Liberty, Mitaka and Newton releases of OpenStack +# The list can be generated using the Rest API from RefStack project: +# https://refstack.openstack.org/api/v1/guidelines/2016.08/tests?target=compute&type=required&alias=true&flag=false tempest.api.compute.images.test_images_oneserver.ImagesOneServerTestJSON.test_create_delete_image[id-3731d080-d4c5-4872-b41a-64d0d0021314] tempest.api.compute.images.test_images_oneserver.ImagesOneServerTestJSON.test_create_image_specify_multibyte_character_image_name[id-3b7c6fe4-dfe7-477c-9243-b06359db51e6] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_filter_by_changes_since[id-18bac3ae-da27-436c-92a9-b22474d13aab] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_filter_by_name[id-33163b73-79f5-4d07-a7ea-9213bcc468ff] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_filter_by_server_id[id-9f238683-c763-45aa-b848-232ec3ce3105] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_filter_by_server_ref[id-05a377b8-28cf-4734-a1e6-2ab5c38bf606] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_filter_by_status[id-a3f5b513-aeb3-42a9-b18e-f091ef73254d] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_filter_by_type[id-e3356918-4d3e-4756-81d5-abc4524ba29f] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_limit_results[id-3a484ca9-67ba-451e-b494-7fcf28d32d62] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_with_detail_filter_by_changes_since[id-7d439e18-ac2e-4827-b049-7e18004712c4] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_with_detail_filter_by_name[id-644ea267-9bd9-4f3b-af9f-dffa02396a17] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_with_detail_filter_by_server_ref[id-8c78f822-203b-4bf6-8bba-56ebd551cf84] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_with_detail_filter_by_status[id-9b0ea018-6185-4f71-948a-a123a107988e] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_with_detail_filter_by_type[id-888c0cc0-7223-43c5-9db0-b125fd0a393b] -tempest.api.compute.images.test_list_image_filters.ListImageFiltersTestJSON.test_list_images_with_detail_limit_results[id-ba2fa9a9-b672-47cc-b354-3b4c0600e2cb] -tempest.api.compute.images.test_list_images.ListImagesTestJSON.test_get_image[id-490d0898-e12a-463f-aef0-c50156b9f789] -tempest.api.compute.images.test_list_images.ListImagesTestJSON.test_list_images[id-fd51b7f4-d4a3-4331-9885-866658112a6f] -tempest.api.compute.images.test_list_images.ListImagesTestJSON.test_list_images_with_detail[id-9f94cb6b-7f10-48c5-b911-a0b84d7d4cd6] tempest.api.compute.servers.test_create_server.ServersTestJSON.test_host_name_is_same_as_server_name[id-ac1ad47f-984b-4441-9274-c9079b7a0666] -tempest.api.compute.servers.test_create_server.ServersTestJSON.test_list_servers[id-9a438d88-10c6-4bcd-8b5b-5b6e25e1346f,smoke] +tempest.api.compute.servers.test_create_server.ServersTestJSON.test_list_servers[id-9a438d88-10c6-4bcd-8b5b-5b6e25e1346f] tempest.api.compute.servers.test_create_server.ServersTestJSON.test_list_servers_with_detail[id-585e934c-448e-43c4-acbf-d06a9b899997] tempest.api.compute.servers.test_create_server.ServersTestJSON.test_verify_created_server_vcpus[id-cbc0f52f-05aa-492b-bdc1-84b575ca294b] -tempest.api.compute.servers.test_create_server.ServersTestJSON.test_verify_server_details[id-5de47127-9977-400a-936f-abcfbec1218f,smoke] +tempest.api.compute.servers.test_create_server.ServersTestJSON.test_verify_server_details[id-5de47127-9977-400a-936f-abcfbec1218f] tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_host_name_is_same_as_server_name[id-ac1ad47f-984b-4441-9274-c9079b7a0666] -tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_list_servers[id-9a438d88-10c6-4bcd-8b5b-5b6e25e1346f,smoke] +tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_list_servers[id-9a438d88-10c6-4bcd-8b5b-5b6e25e1346f] tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_list_servers_with_detail[id-585e934c-448e-43c4-acbf-d06a9b899997] tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_verify_created_server_vcpus[id-cbc0f52f-05aa-492b-bdc1-84b575ca294b] -tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_verify_server_details[id-5de47127-9977-400a-936f-abcfbec1218f,smoke] +tempest.api.compute.servers.test_create_server.ServersTestManualDisk.test_verify_server_details[id-5de47127-9977-400a-936f-abcfbec1218f] tempest.api.compute.servers.test_instance_actions.InstanceActionsTestJSON.test_get_instance_action[id-aacc71ca-1d70-4aa5-bbf6-0ff71470e43c] tempest.api.compute.servers.test_instance_actions.InstanceActionsTestJSON.test_list_instance_actions[id-77ca5cc5-9990-45e0-ab98-1de8fead201a] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_detailed_filter_by_flavor[id-80c574cc-0925-44ba-8602-299028357dd9] @@ -37,31 +21,28 @@ tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.t tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_detailed_filter_by_server_name[id-f9eb2b70-735f-416c-b260-9914ac6181e4] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_detailed_filter_by_server_status[id-de2612ab-b7dd-4044-b0b1-d2539601911f] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_detailed_limit_results[id-67aec2d0-35fe-4503-9f92-f13272b867ed] +tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filter_by_active_status[id-ca78e20e-fddb-4ce6-b7f7-bcbf8605e66e] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filter_by_flavor[id-573637f5-7325-47bb-9144-3476d0416908] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filter_by_image[id-05e8a8e7-9659-459a-989d-92c2f501f4ba] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filter_by_limit[id-614cdfc1-d557-4bac-915b-3e67b48eee76] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filter_by_server_name[id-9b067a7b-7fee-4f6a-b29c-be43fe18fc5a] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filter_by_server_status[id-ca78e20e-fddb-4ce6-b7f7-bcbf8605e66e] -tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filtered_by_ip[id-43a1242e-7b31-48d1-88f2-3f72aa9f2077] -tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filtered_by_ip_regex[id-a905e287-c35e-42f2-b132-d02b09f3654a] tempest.api.compute.servers.test_list_server_filters.ListServerFiltersTestJSON.test_list_servers_filtered_by_name_wildcard[id-e9f624ee-92af-4562-8bec-437945a18dcb] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_changes_since_future_date[id-74745ad8-b346-45b5-b9b8-509d7447fc1f,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_changes_since_invalid_date[id-87d12517-e20a-4c9c-97b6-dd1628d6d6c9,negative] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_changes_since_future_date[id-74745ad8-b346-45b5-b9b8-509d7447fc1f] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_changes_since_invalid_date[id-87d12517-e20a-4c9c-97b6-dd1628d6d6c9] tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits[id-12c80a9f-2dec-480e-882b-98ba15757659] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits_greater_than_actual_count[id-d47c17fb-eebd-4287-8e95-f20a7e627b18,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits_pass_negative_value[id-62610dd9-4713-4ee0-8beb-fd2c1aa7f950,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits_pass_string[id-679bc053-5e70-4514-9800-3dfab1a380a6,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_non_existing_flavor[id-5913660b-223b-44d4-a651-a0fbfd44ca75,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_non_existing_image[id-ff01387d-c7ad-47b4-ae9e-64fa214638fe,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_non_existing_server_name[id-e2c77c4a-000a-4af3-a0bd-629a328bde7c,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_detail_server_is_deleted[id-93055106-2d34-46fe-af68-d9ddbf7ee570,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_status_non_existing[id-fcdf192d-0f74-4d89-911f-1ec002b822c4,negative] -tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_with_a_deleted_server[id-24a26f1a-1ddc-4eea-b0d7-a90cc874ad8f,negative] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits_greater_than_actual_count[id-d47c17fb-eebd-4287-8e95-f20a7e627b18] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits_pass_negative_value[id-62610dd9-4713-4ee0-8beb-fd2c1aa7f950] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_limits_pass_string[id-679bc053-5e70-4514-9800-3dfab1a380a6] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_non_existing_flavor[id-5913660b-223b-44d4-a651-a0fbfd44ca75] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_non_existing_image[id-ff01387d-c7ad-47b4-ae9e-64fa214638fe] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_by_non_existing_server_name[id-e2c77c4a-000a-4af3-a0bd-629a328bde7c] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_detail_server_is_deleted[id-93055106-2d34-46fe-af68-d9ddbf7ee570] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_status_non_existing[id-fcdf192d-0f74-4d89-911f-1ec002b822c4] +tempest.api.compute.servers.test_list_servers_negative.ListServersNegativeTestJSON.test_list_servers_with_a_deleted_server[id-24a26f1a-1ddc-4eea-b0d7-a90cc874ad8f] tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_lock_unlock_server[id-80a8094c-211e-440a-ab88-9e59d556c7ee] -tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_hard[id-2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32,smoke] +tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_hard[id-2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32] tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_rebuild_server[id-aaa6cdf3-55a7-461a-add9-1c8596b9a07c] -tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm[id-1499262a-9328-4eda-9068-db1ac57498d2] -tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_revert[id-c03aab19-adb1-44f5-917d-c419577e9e68] tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_stop_start_server[id-af8eafd4-38a7-4a4b-bdbc-75145a580560] tempest.api.compute.servers.test_server_metadata.ServerMetadataTestJSON.test_delete_server_metadata_item[id-127642d6-4c7b-4486-b7cd-07265a378658] tempest.api.compute.servers.test_server_metadata.ServerMetadataTestJSON.test_get_server_metadata_item[id-3043c57d-7e0e-49a6-9a96-ad569c265e6a] @@ -74,49 +55,194 @@ tempest.api.compute.servers.test_servers.ServersTestJSON.test_create_specify_key tempest.api.compute.servers.test_servers.ServersTestJSON.test_create_with_existing_server_name[id-8fea6be7-065e-47cf-89b8-496e6f96c699] tempest.api.compute.servers.test_servers.ServersTestJSON.test_update_access_server_address[id-89b90870-bc13-4b73-96af-f9d4f2b70077] tempest.api.compute.servers.test_servers.ServersTestJSON.test_update_server_name[id-5e6ccff8-349d-4852-a8b3-055df7988dd2] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_numeric_server_name[id-fd57f159-68d6-4c2a-902b-03070828a87e,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_server_metadata_exceeds_length_limit[id-7fc74810-0bd2-4cd7-8244-4f33a9db865a,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_server_name_length_exceeds_256[id-c3e0fb12-07fc-4d76-a22e-37409887afe8,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_with_invalid_flavor[id-18f5227f-d155-4429-807c-ccb103887537,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_with_invalid_image[id-fcba1052-0a50-4cf3-b1ac-fae241edf02f,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_with_invalid_network_uuid[id-4e72dc2d-44c5-4336-9667-f7972e95c402,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_delete_server_pass_id_exceeding_length_limit[id-f4d7279b-5fd2-4bf2-9ba4-ae35df0d18c5,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_delete_server_pass_negative_id[id-75f79124-277c-45e6-a373-a1d6803f4cc4,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_get_non_existent_server[id-3436b02f-1b1e-4f03-881e-c6a602327439,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_invalid_ip_v6_address[id-5226dd80-1e9c-4d8a-b5f9-b26ca4763fd0,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server[id-d4c023a0-9c55-4747-9dd5-413b820143c7,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_rebuild_deleted_server[id-98fa0458-1485-440f-873b-fe7f0d714930,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_rebuild_non_existent_server[id-d86141a7-906e-4731-b187-d64a2ea61422,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_resize_server_with_non_existent_flavor[id-ced1a1d7-2ab6-45c9-b90f-b27d87b30efd,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_resize_server_with_null_flavor[id-45436a7d-a388-4a35-a9d8-3adc5d0d940b,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_server_name_blank[id-dbbfd247-c40c-449e-8f6c-d2aa7c7da7cf,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_stop_non_existent_server[id-a31460a9-49e1-42aa-82ee-06e0bb7c2d03,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_update_name_of_non_existent_server[id-aa8eed43-e2cb-4ebf-930b-da14f6a21d81,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_update_server_name_length_exceeds_256[id-5c8e244c-dada-4590-9944-749c455b431f,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_update_server_set_empty_name[id-38204696-17c6-44da-9590-40f87fb5a899,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestMultiTenantJSON.test_delete_a_server_of_another_tenant[id-5c75009d-3eea-423e-bea3-61b09fd25f9c,negative] -tempest.api.compute.servers.test_servers_negative.ServersNegativeTestMultiTenantJSON.test_update_server_of_another_tenant[id-543d84c1-dd2e-4c6d-8cb2-b9da0efaa384,negative] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_numeric_server_name[id-fd57f159-68d6-4c2a-902b-03070828a87e] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_server_metadata_exceeds_length_limit[id-7fc74810-0bd2-4cd7-8244-4f33a9db865a] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_server_name_length_exceeds_256[id-c3e0fb12-07fc-4d76-a22e-37409887afe8] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_with_invalid_flavor[id-18f5227f-d155-4429-807c-ccb103887537] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_with_invalid_image[id-fcba1052-0a50-4cf3-b1ac-fae241edf02f] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_create_with_invalid_network_uuid[id-4e72dc2d-44c5-4336-9667-f7972e95c402] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_delete_server_pass_id_exceeding_length_limit[id-f4d7279b-5fd2-4bf2-9ba4-ae35df0d18c5] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_delete_server_pass_negative_id[id-75f79124-277c-45e6-a373-a1d6803f4cc4] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_get_non_existent_server[id-3436b02f-1b1e-4f03-881e-c6a602327439] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_invalid_ip_v6_address[id-5226dd80-1e9c-4d8a-b5f9-b26ca4763fd0] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_reboot_non_existent_server[id-d4c023a0-9c55-4747-9dd5-413b820143c7] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_rebuild_deleted_server[id-98fa0458-1485-440f-873b-fe7f0d714930] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_rebuild_non_existent_server[id-d86141a7-906e-4731-b187-d64a2ea61422] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_rebuild_reboot_deleted_server[id-98fa0458-1485-440f-873b-fe7f0d714930] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_server_name_blank[id-dbbfd247-c40c-449e-8f6c-d2aa7c7da7cf] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_stop_non_existent_server[id-a31460a9-49e1-42aa-82ee-06e0bb7c2d03] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_update_name_of_non_existent_server[id-aa8eed43-e2cb-4ebf-930b-da14f6a21d81] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_update_server_name_length_exceeds_256[id-5c8e244c-dada-4590-9944-749c455b431f] +tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_update_server_set_empty_name[id-38204696-17c6-44da-9590-40f87fb5a899] tempest.api.compute.test_quotas.QuotasTestJSON.test_get_default_quotas[id-9bfecac7-b966-4f47-913f-1a9e2c12134a] tempest.api.compute.test_quotas.QuotasTestJSON.test_get_quotas[id-f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107] +tempest.api.compute.test_versions.TestVersions.test_list_api_versions[id-6c0a0990-43b6-4529-9b61-5fd8daf7c55c] tempest.api.compute.volumes.test_attach_volume.AttachVolumeTestJSON.test_attach_detach_volume[id-52e9045a-e90d-4c0d-9087-79d657faffff] tempest.api.compute.volumes.test_attach_volume.AttachVolumeTestJSON.test_list_get_volume_attachments[id-7fa563fe-f0f7-43eb-9e22-a1ece036b513] -tempest.api.compute.volumes.test_volumes_list.VolumesTestJSON.test_volume_list[id-bc2dd1a0-15af-48e5-9990-f2e75a48325d] -tempest.api.compute.volumes.test_volumes_list.VolumesTestJSON.test_volume_list_with_details[id-bad0567a-5a4f-420b-851e-780b55bb867c] -tempest.api.compute.volumes.test_volumes_negative.VolumesNegativeTest.test_get_invalid_volume_id[id-f01904f2-e975-4915-98ce-cb5fa27bde4f,negative] -tempest.api.compute.volumes.test_volumes_negative.VolumesNegativeTest.test_get_volume_without_passing_volume_id[id-62bab09a-4c03-4617-8cca-8572bc94af9b,negative] +tempest.api.identity.v3.TestApiDiscovery.test_api_media_types[id-657c1970-4722-4189-8831-7325f3bc4265] +tempest.api.identity.v3.TestApiDiscovery.test_api_version_resources[id-b9232f5e-d9e5-4d97-b96c-28d3db4de1bd] +tempest.api.identity.v3.TestApiDiscovery.test_api_version_statuses[id-8879a470-abfb-47bb-bb8d-5a7fd279ad1e] +tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_api_media_types[id-657c1970-4722-4189-8831-7325f3bc4265] +tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_api_version_resources[id-b9232f5e-d9e5-4d97-b96c-28d3db4de1bd] +tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_api_version_statuses[id-8879a470-abfb-47bb-bb8d-5a7fd279ad1e] tempest.api.identity.v3.test_tokens.TokensV3Test.test_create_token[id-6f8e4436-fc96-4282-8122-e41df57197a9] +tempest.api.image.v2.test_images.BasicOperationsImagesTest.test_delete_image[id-f848bb94-1c6e-45a4-8726-39e3a5b23535] +tempest.api.image.v2.test_images.BasicOperationsImagesTest.test_update_image[id-f66891a7-a35c-41a8-b590-a065c2a1caa6] +tempest.api.image.v2.test_images.ListImagesTest.test_get_image_schema[id-622b925c-479f-4736-860d-adeaf13bc371] +tempest.api.image.v2.test_images.ListImagesTest.test_get_images_schema[id-25c8d7b2-df21-460f-87ac-93130bcdc684] +tempest.api.image.v2.test_images.ListImagesTest.test_index_no_params[id-1e341d7a-90a9-494c-b143-2cdf2aeb6aee] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_container_format[id-9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_disk_format[id-4a4735a7-f22f-49b6-b0d9-66e1ef7453eb] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_limit[id-e914a891-3cc8-4b40-ad32-e0a39ffbddbb] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_min_max_size[id-4ad8c157-971a-4ba8-aa84-ed61154b1e7f] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_size[id-cf1b9a48-8340-480e-af7b-fe7e17690876] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_status[id-7fc9e369-0f58-4d05-9aa5-0969e2d59d15] +tempest.api.image.v2.test_images.ListImagesTest.test_list_images_param_visibility[id-7a95bb92-d99e-4b12-9718-7bc6ab73e6d2] tempest.api.image.v2.test_images.ListImagesTest.test_list_no_params[id-1e341d7a-90a9-494c-b143-2cdf2aeb6aee] -tempest.api.image.v1.test_images.ListImagesTest.test_index_no_params[id-246178ab-3b33-4212-9a4b-a7fe8261794d] -tempest.api.object_storage.test_object_expiry.ObjectExpiryTest.test_get_object_after_expiry_time[id-fb024a42-37f3-4ba5-9684-4f40a7910b41] -tempest.api.object_storage.test_object_services.ObjectTest.test_copy_object_2d_way[id-06f90388-2d0e-40aa-934c-e9a8833e958a] -tempest.api.object_storage.test_object_services.ObjectTest.test_copy_object_across_containers[id-aa467252-44f3-472a-b5ae-5b57c3c9c147] -tempest.api.object_storage.test_object_services.ObjectTest.test_copy_object_in_same_container[id-1a9ab572-1b66-4981-8c21-416e2a5e6011] -tempest.api.object_storage.test_object_services.ObjectTest.test_copy_object_to_itself[id-2248abba-415d-410b-9c30-22dff9cd6e67] -tempest.api.object_storage.test_object_services.ObjectTest.test_create_object[id-5b4ce26f-3545-46c9-a2ba-5754358a4c62,smoke] -tempest.api.object_storage.test_object_services.ObjectTest.test_delete_object[id-17738d45-03bd-4d45-9e0b-7b2f58f98687] -tempest.api.object_storage.test_object_services.ObjectTest.test_get_object[id-02610ba7-86b7-4272-9ed8-aa8d417cb3cd,smoke] -tempest.api.object_storage.test_object_services.ObjectTest.test_get_object_if_different[id-50d01f12-526f-4360-9ac2-75dd508d7b68] -tempest.api.object_storage.test_object_services.ObjectTest.test_object_upload_in_segments[id-e3e6a64a-9f50-4955-b987-6ce6767c97fb] -tempest.api.object_storage.test_object_temp_url.ObjectTempUrlTest.test_get_object_using_temp_url[id-f91c96d4-1230-4bba-8eb9-84476d18d991] -tempest.api.object_storage.test_object_temp_url.ObjectTempUrlTest.test_put_object_using_temp_url[id-9b08dade-3571-4152-8a4f-a4f2a873a735] -tempest.api.object_storage.test_object_version.ContainerTest.test_versioned_container[id-a151e158-dcbf-4a1f-a1e7-46cd65895a6f] +tempest.api.image.v2.test_images.ListUserImagesTest.test_get_image_schema[id-622b925c-479f-4736-860d-adeaf13bc371] +tempest.api.image.v2.test_images.ListUserImagesTest.test_get_images_schema[id-25c8d7b2-df21-460f-87ac-93130bcdc684] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_container_format[id-9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_disk_format[id-4a4735a7-f22f-49b6-b0d9-66e1ef7453eb] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_limit[id-e914a891-3cc8-4b40-ad32-e0a39ffbddbb] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_min_max_size[id-4ad8c157-971a-4ba8-aa84-ed61154b1e7f] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_size[id-cf1b9a48-8340-480e-af7b-fe7e17690876] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_status[id-7fc9e369-0f58-4d05-9aa5-0969e2d59d15] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_images_param_visibility[id-7a95bb92-d99e-4b12-9718-7bc6ab73e6d2] +tempest.api.image.v2.test_images.ListUserImagesTest.test_list_no_params[id-1e341d7a-90a9-494c-b143-2cdf2aeb6aee] +tempest.api.image.v2.test_images_negative.ImagesNegativeTest.test_delete_image_null_id[id-32248db1-ab88-4821-9604-c7c369f1f88c] +tempest.api.image.v2.test_images_negative.ImagesNegativeTest.test_delete_non_existing_image[id-6fe40f1c-57bd-4918-89cc-8500f850f3de] +tempest.api.image.v2.test_images_negative.ImagesNegativeTest.test_get_delete_deleted_image[id-e57fc127-7ba0-4693-92d7-1d8a05ebcba9] +tempest.api.image.v2.test_images_negative.ImagesNegativeTest.test_get_image_null_id[id-ef45000d-0a72-4781-866d-4cb7bf2562ad] +tempest.api.image.v2.test_images_negative.ImagesNegativeTest.test_get_non_existent_image[id-668743d5-08ad-4480-b2b8-15da34f81d9f] +tempest.api.image.v2.test_images_tags.ImagesTagsTest.test_update_delete_tags_for_image[id-10407036-6059-4f95-a2cd-cbbbee7ed329] +tempest.api.image.v2.test_images_tags_negative.ImagesTagsNegativeTest.test_delete_non_existing_tag[id-39c023a2-325a-433a-9eea-649bf1414b19] +tempest.api.image.v2.test_images_tags_negative.ImagesTagsNegativeTest.test_update_tags_for_non_existing_image[id-8cd30f82-6f9a-4c6e-8034-c1b51fba43d9] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_all_attributes[id-a4d9ec4c-0306-4111-a75c-db01a709030b] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_with_allocation_pools[id-bec949c4-3147-4ba6-af5f-cd2306118404] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_with_dhcp_enabled[id-94ce038d-ff0a-4a4c-a56b-09da3ca0b55d] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_with_gw[id-9393b468-186d-496d-aa36-732348cd76e7] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_with_gw_and_allocation_pools[id-8217a149-0c6c-4cfb-93db-0486f707d13f] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_with_host_routes_and_dns_nameservers[id-d830de0a-be47-468f-8f02-1fd996118289] +tempest.api.network.test_networks.NetworksTest.test_create_delete_subnet_without_gateway[id-d2d596e2-8e76-47a9-ac51-d4648009f4d3] +tempest.api.network.test_networks.NetworksTest.test_create_update_delete_network_subnet[id-0e269138-0da6-4efc-a46d-578161e7b221] +tempest.api.network.test_networks.NetworksTest.test_delete_network_with_subnet[id-f04f61a9-b7f3-4194-90b2-9bcf660d1bfe] +tempest.api.network.test_networks.NetworksTest.test_list_networks[id-f7ffdeda-e200-4a7a-bcbe-05716e86bf43] +tempest.api.network.test_networks.NetworksTest.test_list_networks_fields[id-6ae6d24f-9194-4869-9c85-c313cb20e080] +tempest.api.network.test_networks.NetworksTest.test_list_subnets[id-db68ba48-f4ea-49e9-81d1-e367f6d0b20a] +tempest.api.network.test_networks.NetworksTest.test_list_subnets_fields[id-842589e3-9663-46b0-85e4-7f01273b0412] +tempest.api.network.test_networks.NetworksTest.test_show_network[id-2bf13842-c93f-4a69-83ed-717d2ec3b44e] +tempest.api.network.test_networks.NetworksTest.test_show_network_fields[id-867819bb-c4b6-45f7-acf9-90edcf70aa5e] +tempest.api.network.test_networks.NetworksTest.test_show_subnet[id-bd635d81-6030-4dd1-b3b9-31ba0cfdf6cc] +tempest.api.network.test_networks.NetworksTest.test_show_subnet_fields[id-270fff0b-8bfc-411f-a184-1e8fd35286f0] +tempest.api.network.test_networks.NetworksTest.test_update_subnet_gw_dns_host_routes_dhcp[id-3d3852eb-3009-49ec-97ac-5ce83b73010a] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_all_attributes[id-a4d9ec4c-0306-4111-a75c-db01a709030b] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_with_allocation_pools[id-bec949c4-3147-4ba6-af5f-cd2306118404] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_with_dhcp_enabled[id-94ce038d-ff0a-4a4c-a56b-09da3ca0b55d] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_with_gw[id-9393b468-186d-496d-aa36-732348cd76e7] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_with_gw_and_allocation_pools[id-8217a149-0c6c-4cfb-93db-0486f707d13f] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_with_host_routes_and_dns_nameservers[id-d830de0a-be47-468f-8f02-1fd996118289] +tempest.api.network.test_networks.NetworksTestJSON.test_create_delete_subnet_without_gateway[id-d2d596e2-8e76-47a9-ac51-d4648009f4d3] +tempest.api.network.test_networks.NetworksTestJSON.test_create_update_delete_network_subnet[id-0e269138-0da6-4efc-a46d-578161e7b221] +tempest.api.network.test_networks.NetworksTestJSON.test_delete_network_with_subnet[id-f04f61a9-b7f3-4194-90b2-9bcf660d1bfe] +tempest.api.network.test_networks.NetworksTestJSON.test_list_networks[id-f7ffdeda-e200-4a7a-bcbe-05716e86bf43] +tempest.api.network.test_networks.NetworksTestJSON.test_list_networks_fields[id-6ae6d24f-9194-4869-9c85-c313cb20e080] +tempest.api.network.test_networks.NetworksTestJSON.test_list_subnets[id-db68ba48-f4ea-49e9-81d1-e367f6d0b20a] +tempest.api.network.test_networks.NetworksTestJSON.test_list_subnets_fields[id-842589e3-9663-46b0-85e4-7f01273b0412] +tempest.api.network.test_networks.NetworksTestJSON.test_show_network[id-2bf13842-c93f-4a69-83ed-717d2ec3b44e] +tempest.api.network.test_networks.NetworksTestJSON.test_show_network_fields[id-867819bb-c4b6-45f7-acf9-90edcf70aa5e] +tempest.api.network.test_networks.NetworksTestJSON.test_show_subnet[id-bd635d81-6030-4dd1-b3b9-31ba0cfdf6cc] +tempest.api.network.test_networks.NetworksTestJSON.test_show_subnet_fields[id-270fff0b-8bfc-411f-a184-1e8fd35286f0] +tempest.api.network.test_networks.NetworksTestJSON.test_update_subnet_gw_dns_host_routes_dhcp[id-3d3852eb-3009-49ec-97ac-5ce83b73010a] +tempest.api.network.test_ports.PortsTestJSON.test_create_bulk_port[id-67f1b811-f8db-43e2-86bd-72c074d4a42c] +tempest.api.network.test_ports.PortsTestJSON.test_create_port_in_allowed_allocation_pools[id-0435f278-40ae-48cb-a404-b8a087bc09b1] +tempest.api.network.test_ports.PortsTestJSON.test_create_update_delete_port[id-c72c1c0c-2193-4aca-aaa4-b1442640f51c] +tempest.api.network.test_ports.PortsTestJSON.test_list_ports[id-cf95b358-3e92-4a29-a148-52445e1ac50e] +tempest.api.network.test_ports.PortsTestJSON.test_list_ports_fields[id-ff7f117f-f034-4e0e-abff-ccef05c454b4] +tempest.api.network.test_ports.PortsTestJSON.test_show_port[id-c9a685bd-e83f-499c-939f-9f7863ca259f] +tempest.api.network.test_ports.PortsTestJSON.test_show_port_fields[id-45fcdaf2-dab0-4c13-ac6c-fcddfb579dbd] +tempest.api.network.test_ports.PortsTestJSON.test_update_port_with_security_group_and_extra_attributes[id-58091b66-4ff4-4cc1-a549-05d60c7acd1a] +tempest.api.network.test_ports.PortsTestJSON.test_update_port_with_two_security_groups_and_extra_attributes[id-edf6766d-3d40-4621-bc6e-2521a44c257d] +tempest.api.network.test_security_groups.SecGroupTest.test_create_list_update_show_delete_security_group[id-bfd128e5-3c92-44b6-9d66-7fe29d22c802] +tempest.api.network.test_security_groups.SecGroupTest.test_create_security_group_rule_with_additional_args[id-87dfbcf9-1849-43ea-b1e4-efa3eeae9f71] +tempest.api.network.test_security_groups.SecGroupTest.test_create_security_group_rule_with_icmp_type_code[id-c9463db8-b44d-4f52-b6c0-8dbda99f26ce] +tempest.api.network.test_security_groups.SecGroupTest.test_create_security_group_rule_with_protocol_integer_value[id-0a307599-6655-4220-bebc-fd70c64f2290] +tempest.api.network.test_security_groups.SecGroupTest.test_create_security_group_rule_with_remote_group_id[id-c2ed2deb-7a0c-44d8-8b4c-a5825b5c310b] +tempest.api.network.test_security_groups.SecGroupTest.test_create_security_group_rule_with_remote_ip_prefix[id-16459776-5da2-4634-bce4-4b55ee3ec188] +tempest.api.network.test_security_groups.SecGroupTest.test_create_show_delete_security_group_rule[id-cfb99e0e-7410-4a3d-8a0c-959a63ee77e9] +tempest.api.network.test_security_groups.SecGroupTest.test_list_security_groups[id-e30abd17-fef9-4739-8617-dc26da88e686] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_additional_default_security_group_fails[id-2323061e-9fbf-4eb0-b547-7e8fafc90849] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_duplicate_security_group_rule_fails[id-8fde898f-ce88-493b-adc9-4e4692879fc5] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_security_group_rule_with_bad_ethertype[id-5666968c-fff3-40d6-9efc-df1c8bd01abb] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_security_group_rule_with_bad_protocol[id-981bdc22-ce48-41ed-900a-73148b583958] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_security_group_rule_with_bad_remote_ip_prefix[id-5f8daf69-3c5f-4aaa-88c9-db1d66f68679] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_security_group_rule_with_invalid_ports[id-0d9c7791-f2ad-4e2f-ac73-abf2373b0d2d] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_security_group_rule_with_non_existent_remote_groupid[id-4bf786fd-2f02-443c-9716-5b98e159a49a] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_create_security_group_rule_with_non_existent_security_group[id-be308db6-a7cf-4d5c-9baf-71bafd73f35e] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_delete_non_existent_security_group[id-1f1bb89d-5664-4956-9fcd-83ee0fa603df] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_show_non_existent_security_group[id-424fd5c3-9ddc-486a-b45f-39bf0c820fc6] +tempest.api.network.test_security_groups_negative.NegativeSecGroupTest.test_show_non_existent_security_group_rule[id-4c094c09-000b-4e41-8100-9617600c02a6] +tempest.api.volume.test_availability_zone.AvailabilityZoneV2TestJSON.test_get_availability_zone_list[id-01f1ae88-eba9-4c6b-a011-6f7ace06b725] +tempest.api.volume.test_extensions.ExtensionsV2TestJSON.test_list_extensions[id-94607eb0-43a5-47ca-82aa-736b41bd2e2c] +tempest.api.volume.test_snapshot_metadata.SnapshotV2MetadataTestJSON.test_create_get_delete_snapshot_metadata[id-a2f20f99-e363-4584-be97-bc33afb1a56c] +tempest.api.volume.test_snapshot_metadata.SnapshotV2MetadataTestJSON.test_crud_snapshot_metadata[id-a2f20f99-e363-4584-be97-bc33afb1a56c] +tempest.api.volume.test_snapshot_metadata.SnapshotV2MetadataTestJSON.test_update_snapshot_metadata_item[id-e8ff85c5-8f97-477f-806a-3ac364a949ed] +tempest.api.volume.test_volume_metadata.VolumesV2MetadataTest.test_create_get_delete_volume_metadata[id-6f5b125b-f664-44bf-910f-751591fe5769] +tempest.api.volume.test_volume_metadata.VolumesV2MetadataTest.test_crud_volume_metadata[id-6f5b125b-f664-44bf-910f-751591fe5769] +tempest.api.volume.test_volume_metadata.VolumesV2MetadataTest.test_update_volume_metadata_item[id-862261c5-8df4-475a-8c21-946e50e36a20] +tempest.api.volume.test_volumes_actions.VolumesV2ActionsTest.test_attach_detach_volume_to_instance[id-fff42874-7db5-4487-a8e1-ddda5fb5288d] +tempest.api.volume.test_volumes_actions.VolumesV2ActionsTest.test_get_volume_attachment[id-9516a2c8-9135-488c-8dd6-5677a7e5f371] +tempest.api.volume.test_volumes_actions.VolumesV2ActionsTest.test_reserve_unreserve_volume[id-92c4ef64-51b2-40c0-9f7e-4749fbaaba33] +tempest.api.volume.test_volumes_actions.VolumesV2ActionsTest.test_volume_bootable[id-63e21b4c-0a0c-41f6-bfc3-7c2816815599] +tempest.api.volume.test_volumes_actions.VolumesV2ActionsTest.test_volume_readonly_update[id-fff74e1e-5bd3-4b33-9ea9-24c103bc3f59] +tempest.api.volume.test_volumes_get.VolumesV2GetTest.test_volume_create_get_update_delete[id-27fb0e9f-fb64-41dd-8bdb-1ffa762f0d51] +tempest.api.volume.test_volumes_get.VolumesV2GetTest.test_volume_create_get_update_delete_as_clone[id-3f591b4a-7dc6-444c-bd51-77469506b3a1] +tempest.api.volume.test_volumes_get.VolumesV2GetTest.test_volume_create_get_update_delete_from_image[id-54a01030-c7fc-447c-86ee-c1182beae638] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list[id-0b6ddd39-b948-471f-8038-4787978747c4] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_by_name[id-a28e8da4-0b56-472f-87a8-0f4d3f819c02] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_details_by_name[id-2de3a6d4-12aa-403b-a8f2-fdeb42a89623] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_param_display_name_and_status[id-777c87c1-2fc4-4883-8b8e-5c0b951d1ec8] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_with_detail_param_display_name_and_status[id-856ab8ca-6009-4c37-b691-be1065528ad4] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_with_detail_param_metadata[id-1ca92d3c-4a8e-4b43-93f5-e4c7fb3b291d] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_with_details[id-adcbb5a7-5ad8-4b61-bd10-5380e111a877] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_with_param_metadata[id-b5ebea1b-0603-40a0-bb41-15fcd0a53214] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volumes_list_by_availability_zone[id-c0cfa863-3020-40d7-b587-e35f597d5d87] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volumes_list_by_status[id-39654e13-734c-4dab-95ce-7613bf8407ce] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volumes_list_details_by_availability_zone[id-e1b80d13-94f0-4ba2-a40e-386af29f8db1] +tempest.api.volume.test_volumes_list.VolumesV2ListTestJSON.test_volumes_list_details_by_status[id-2943f712-71ec-482a-bf49-d5ca06216b9f] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_attach_volumes_with_nonexistent_volume_id[id-f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_invalid_size[id-1ed83a8a-682d-4dfb-a30e-ee63ffd6c049] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_nonexistent_snapshot_id[id-0c36f6ae-4604-4017-b0a9-34fdc63096f9] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_nonexistent_source_volid[id-47c73e08-4be8-45bb-bfdf-0c4e79b88344] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_nonexistent_volume_type[id-10254ed8-3849-454e-862e-3ab8e6aa01d2] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_out_passing_size[id-9387686f-334f-4d31-a439-33494b9e2683] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_size_negative[id-8b472729-9eba-446e-a83b-916bdb34bef7] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_with_size_zero[id-41331caa-eaf4-4001-869d-bc18c1869360] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_create_volume_without_passing_size[id-9387686f-334f-4d31-a439-33494b9e2683] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_delete_invalid_volume_id[id-1f035827-7c32-4019-9240-b4ec2dbd9dfd] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_delete_volume_without_passing_volume_id[id-441a1550-5d44-4b30-af0f-a6d402f52026] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_detach_volumes_with_invalid_volume_id[id-9f9c24e4-011d-46b5-b992-952140ce237a] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_get_invalid_volume_id[id-30799cfd-7ee4-446c-b66c-45b383ed211b] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_get_volume_without_passing_volume_id[id-c6c3db06-29ad-4e91-beb0-2ab195fe49e3] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_list_volumes_detail_with_invalid_status[id-ba94b27b-be3f-496c-a00e-0283b373fa75] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_list_volumes_detail_with_nonexistent_name[id-9ca17820-a0e7-4cbd-a7fa-f4468735e359] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_list_volumes_with_invalid_status[id-143b279b-7522-466b-81be-34a87d564a7c] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_list_volumes_with_nonexistent_name[id-0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_reserve_volume_with_negative_volume_status[id-449c4ed2-ecdd-47bb-98dc-072aeccf158c] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_reserve_volume_with_nonexistent_volume_id[id-ac6084c0-0546-45f9-b284-38a367e0e0e2] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_unreserve_volume_with_nonexistent_volume_id[id-eb467654-3dc1-4a72-9b46-47c29d22654c] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_update_volume_with_empty_volume_id[id-72aeca85-57a5-4c1f-9057-f320f9ea575b] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_update_volume_with_invalid_volume_id[id-e66e40d6-65e6-4e75-bdc7-636792fa152d] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_update_volume_with_nonexistent_volume_id[id-0186422c-999a-480e-a026-6a665744c30c] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_volume_delete_nonexistent_volume_id[id-555efa6e-efcd-44ef-8a3b-4a7ca4837a29] +tempest.api.volume.test_volumes_negative.VolumesV2NegativeTest.test_volume_get_nonexistent_volume_id[id-f131c586-9448-44a4-a8b0-54ca838aa43e] +tempest.api.volume.test_volumes_snapshots.VolumesV2SnapshotTestJSON.test_snapshot_create_get_list_update_delete[id-2a8abbe4-d871-46db-b049-c41f5af8216e] +tempest.api.volume.test_volumes_snapshots.VolumesV2SnapshotTestJSON.test_snapshots_list_details_with_params[id-220a1022-1fcd-4a74-a7bd-6b859156cda2] +tempest.api.volume.test_volumes_snapshots.VolumesV2SnapshotTestJSON.test_snapshots_list_with_params[id-59f41f43-aebf-48a9-ab5d-d76340fab32b] +tempest.api.volume.test_volumes_snapshots.VolumesV2SnapshotTestJSON.test_volume_from_snapshot[id-677863d1-3142-456d-b6ac-9924f667a7f4] +tempest.api.volume.test_volumes_snapshots_list.VolumesV2SnapshotListTestJSON.test_snapshots_list_details_with_params[id-220a1022-1fcd-4a74-a7bd-6b859156cda2] +tempest.api.volume.test_volumes_snapshots_list.VolumesV2SnapshotListTestJSON.test_snapshots_list_with_params[id-59f41f43-aebf-48a9-ab5d-d76340fab32b] +tempest.api.volume.test_volumes_snapshots_negative.VolumesV2SnapshotNegativeTestJSON.test_create_snapshot_with_nonexistent_volume_id[id-e3e466af-70ab-4f4b-a967-ab04e3532ea7] +tempest.api.volume.test_volumes_snapshots_negative.VolumesV2SnapshotNegativeTestJSON.test_create_snapshot_without_passing_volume_id[id-bb9da53e-d335-4309-9c15-7e76fd5e4d6d] +tempest.api.volume.v2.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_details_pagination[id-e9138a2c-f67b-4796-8efa-635c196d01de] +tempest.api.volume.v2.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_details_with_multiple_params[id-2a7064eb-b9c3-429b-b888-33928fc5edd3] +tempest.api.volume.v2.test_volumes_list.VolumesV2ListTestJSON.test_volume_list_pagination[id-af55e775-8e4b-4feb-8719-215c43b0238c]
\ No newline at end of file diff --git a/functest/opnfv_tests/openstack/tempest/custom_tests/test_list.txt b/functest/opnfv_tests/openstack/tempest/custom_tests/test_list.txt new file mode 100644 index 00000000..ac4e3728 --- /dev/null +++ b/functest/opnfv_tests/openstack/tempest/custom_tests/test_list.txt @@ -0,0 +1,4 @@ +# This is an empty file to be filled up with the desired tempest test cases +# Examples: +#tempest.scenario.test_server_basic_ops.TestServerBasicOps.test_server_basic_ops +#tempest.scenario.test_network_basic_ops.TestNetworkBasicOps.test_network_basic_ops
\ No newline at end of file diff --git a/functest/opnfv_tests/openstack/tempest/tempest.py b/functest/opnfv_tests/openstack/tempest/tempest.py index e1a223a7..4c96500d 100644 --- a/functest/opnfv_tests/openstack/tempest/tempest.py +++ b/functest/opnfv_tests/openstack/tempest/tempest.py @@ -57,7 +57,7 @@ class TempestCommon(testcase_base.TestcaseBase): CONST.tempest_identity_tenant_name, CONST.tempest_identity_tenant_description) if not tenant_id: - logger.error("Error : Failed to create %s tenant" + logger.error("Failed to create %s tenant" % CONST.tempest_identity_tenant_name) user_id = os_utils.create_user(keystone_client, @@ -65,7 +65,7 @@ class TempestCommon(testcase_base.TestcaseBase): CONST.tempest_identity_user_password, None, tenant_id) if not user_id: - logger.error("Error : Failed to create %s user" % + logger.error("Failed to create %s user" % CONST.tempest_identity_user_name) logger.debug("Creating private network for Tempest suite") @@ -74,8 +74,8 @@ class TempestCommon(testcase_base.TestcaseBase): 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 network_dic is None: + raise Exception('Failed to create private network') if CONST.tempest_use_custom_images: # adding alternative image should be trivial should we need it @@ -83,8 +83,8 @@ class TempestCommon(testcase_base.TestcaseBase): _, 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 self.IMAGE_ID is None: + raise Exception('Failed to create image') if CONST.tempest_use_custom_flavors: # adding alternative flavor should be trivial should we need it @@ -94,10 +94,8 @@ class TempestCommon(testcase_base.TestcaseBase): 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 + if self.FLAVOR_ID is None: + raise Exception('Failed to create flavor') def generate_test_list(self, verifier_repo_dir): logger.debug("Generating test case list...") @@ -109,14 +107,13 @@ class TempestCommon(testcase_base.TestcaseBase): 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 + raise Exception("Tempest test list file %s NOT found." + % conf_utils.TEMPEST_CUSTOM) else: if self.MODE == 'smoke': testr_mode = "smoke" elif self.MODE == 'feature_multisite': - testr_mode = " | grep -i kingbird " + testr_mode = "'[Kk]ingbird'" elif self.MODE == 'full': testr_mode = "" else: @@ -128,8 +125,6 @@ class TempestCommon(testcase_base.TestcaseBase): conf_utils.TEMPEST_RAW_LIST)) 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) @@ -164,7 +159,6 @@ class TempestCommon(testcase_base.TestcaseBase): else: result_file.write(str(cases_line) + '\n') result_file.close() - return testcase_base.TestcaseBase.EX_OK def _parse_verification_id(line): first_pos = line.index("UUID=") + len("UUID=") @@ -217,7 +211,7 @@ class TempestCommon(testcase_base.TestcaseBase): f_env.close() def parse_verifier_result(self): - if not self.VERIFICATION_ID: + if self.VERIFICATION_ID is None: raise Exception('Verification UUID not found') cmd_line = "rally verify show --uuid {}".format(self.VERIFICATION_ID) @@ -274,33 +268,23 @@ class TempestCommon(testcase_base.TestcaseBase): 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(self.DEPLOYMENT_DIR, - self.IMAGE_ID, - self.FLAVOR_ID) - if res != testcase_base.TestcaseBase.EX_OK: - return res - - res = self.generate_test_list(self.VERIFIER_REPO_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.run_verifier_tests() - self.parse_verifier_result() + try: + self.create_tempest_resources() + conf_utils.configure_tempest(self.DEPLOYMENT_DIR, + self.IMAGE_ID, + self.FLAVOR_ID, + self.MODE) + self.generate_test_list(self.VERIFIER_REPO_DIR) + self.apply_tempest_blacklist() + self.run_verifier_tests() + self.parse_verifier_result() + res = testcase_base.TestcaseBase.EX_OK + except Exception as e: + logger.error('Error with run: %s' % e) + res = testcase_base.TestcaseBase.EX_RUN_ERROR self.stop_time = time.time() - - # If we are here, it means that the test case was successfully executed - # criteria is managed by the criteria Field - return testcase_base.TestcaseBase.EX_OK + return res class TempestSmokeSerial(TempestCommon): @@ -336,13 +320,21 @@ class TempestMultisite(TempestCommon): self.case_name = "multisite" self.MODE = "feature_multisite" self.OPTION = "--concurrency 1" - conf_utils.configure_tempest_multisite(self.DEPLOYMENT_DIR) class TempestCustom(TempestCommon): - def __init__(self, mode, option): + def __init__(self): TempestCommon.__init__(self) self.case_name = "tempest_custom" - self.MODE = mode - self.OPTION = option + self.MODE = "custom" + self.OPTION = "--concurrency 1" + + +class TempestDefcore(TempestCommon): + + def __init__(self): + TempestCommon.__init__(self) + self.case_name = "tempest_defcore" + self.MODE = "defcore" + self.OPTION = "--concurrency 1" diff --git a/functest/opnfv_tests/openstack/vping/vping_base.py b/functest/opnfv_tests/openstack/vping/vping_base.py index 8285d93f..9d57cfae 100644 --- a/functest/opnfv_tests/openstack/vping/vping_base.py +++ b/functest/opnfv_tests/openstack/vping/vping_base.py @@ -32,6 +32,8 @@ class VPingBase(testcase_base.TestcaseBase): 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_username = CONST.openstack_image_username + self.image_password = CONST.openstack_image_password self.image_path = os.path.join(CONST.dir_functest_data, self.image_filename) @@ -289,6 +291,6 @@ class VPingMain(object): if result != VPingBase.EX_OK: return result if kwargs['report']: - return self.vping.publish_report() + return self.vping.push_to_db() except Exception: return VPingBase.EX_RUN_ERROR diff --git a/functest/opnfv_tests/openstack/vping/vping_ssh.py b/functest/opnfv_tests/openstack/vping/vping_ssh.py index b032c308..7a58a41f 100755 --- a/functest/opnfv_tests/openstack/vping/vping_ssh.py +++ b/functest/opnfv_tests/openstack/vping/vping_ssh.py @@ -61,8 +61,6 @@ class VPingSSH(vping_base.VPingBase): def establish_ssh(self, vm, floatip): self.logger.info("Trying to establish SSH connection to %s..." % floatip) - username = 'cirros' - password = 'cubswin:)' ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) @@ -73,8 +71,8 @@ class VPingSSH(vping_base.VPingBase): cidr_first_octet = self.private_subnet_cidr.split('.')[0] while timeout > 0: try: - ssh.connect(floatip, username=username, - password=password, timeout=2) + ssh.connect(floatip, username=self.image_username, + password=self.image_password, timeout=2) self.logger.debug("SSH connection established to %s." % floatip) break diff --git a/functest/opnfv_tests/sdn/odl/odl.py b/functest/opnfv_tests/sdn/odl/odl.py index 25075957..69818f5a 100755 --- a/functest/opnfv_tests/sdn/odl/odl.py +++ b/functest/opnfv_tests/sdn/odl/odl.py @@ -186,7 +186,7 @@ class ODLTests(testcase_base.TestcaseBase): return self.main(suites, **kwargs) -class ODLParser(): +class ODLParser(object): def __init__(self): self.parser = argparse.ArgumentParser() @@ -237,6 +237,6 @@ if __name__ == '__main__': if result != testcase_base.TestcaseBase.EX_OK: sys.exit(result) if args['pushtodb']: - sys.exit(odl.publish_report()) + sys.exit(odl.push_to_db()) except Exception: sys.exit(testcase_base.TestcaseBase.EX_RUN_ERROR) diff --git a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py index 090502ba..c2198690 100644 --- a/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py +++ b/functest/opnfv_tests/sdn/onos/sfc/sfc_onos.py @@ -10,13 +10,15 @@ from pexpect import pxssh import functest.utils.functest_logger as ft_logger +from functest.utils.constants import CONST + OK = 200 CREATED = 201 ACCEPTED = 202 NO_CONTENT = 204 -class SfcOnos: +class SfcOnos(object): """Defines all the def function of SFC.""" def __init__(self): @@ -99,6 +101,8 @@ class SfcOnos: self.ip_pool = 0 self.vm_public_ip = [] self.vm_public_id = [] + self.cirros_username = CONST.openstack_image_username + self.cirros_password = CONST.openstack_image_password self.net_id1 = 0 self.vm = [] self.address = 0 @@ -628,9 +632,7 @@ class SfcOnos: s = pxssh.pxssh() hostname = self.vm_public_ip[0] - username = "cirros" - password = "cubswin:)" - s.login(hostname, username, password) + s.login(hostname, self.cirros_username, self.cirros_password) s.sendline("ping -c 5 " + str(self.port_ip[2])) s.prompt() # match the prompt @@ -644,9 +646,7 @@ class SfcOnos: def vm1(queue1): s = pxssh.pxssh() hostname = self.vm_public_ip[1] - username = "cirros" - password = "cubswin:)" - s.login(hostname, username, password) + s.login(hostname, self.cirros_username, self.cirros_password) s.sendline('sudo ./firewall') s.prompt() output_pack = s.before diff --git a/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py b/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py index bf2c4302..2bef5cc6 100644 --- a/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py +++ b/functest/opnfv_tests/sdn/onos/teston/adapters/foundation.py @@ -21,7 +21,7 @@ import functest.utils.functest_constants as ft_constants import functest.utils.functest_utils as ft_utils -class Foundation: +class Foundation(object): def __init__(self): diff --git a/functest/opnfv_tests/security_scan/config.ini b/functest/opnfv_tests/security_scan/config.ini deleted file mode 100644 index b97de80f..00000000 --- a/functest/opnfv_tests/security_scan/config.ini +++ /dev/null @@ -1,29 +0,0 @@ -[undercloud] -port = 22 -user = stack -remotekey = /home/stack/.ssh/id_rsa -localkey = /root/.ssh/overCloudKey - -[controller] -port = 22 -user = heat-admin -scantype = xccdf -secpolicy = /usr/share/xml/scap/ssg/content/ssg-centos7-xccdf.xml -cpe = /usr/share/xml/scap/ssg/content/ssg-rhel7-cpe-dictionary.xml -profile = stig-rhel7-server-upstream -report = report.html -results = results.xml -reports_dir=/home/opnfv/functest/results/security_scan/ -clean = True - -[compute] -port = 22 -user = heat-admin -scantype = xccdf -secpolicy = /usr/share/xml/scap/ssg/content/ssg-centos7-xccdf.xml -cpe = /usr/share/xml/scap/ssg/content/ssg-rhel7-cpe-dictionary.xml -profile = sstig-rhel7-server-upstream -report = report.html -results = results.xml -reports_dir=/home/opnfv/functest/results/security_scan/ -clean = True diff --git a/functest/opnfv_tests/security_scan/connect.py b/functest/opnfv_tests/security_scan/connect.py deleted file mode 100644 index 3d5456c5..00000000 --- a/functest/opnfv_tests/security_scan/connect.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2016 Red Hat -# Luke Hinds (lhinds@redhat.com) -# 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 -# -# 0.1: OpenSCAP paramiko connection functions - -import os -import socket -import paramiko - -import functest.utils.functest_logger as ft_logger -import functest.utils.functest_constants as ft_constants - -# add installer IP from env -INSTALLER_IP = ft_constants.CI_INSTALLER_IP - -# Set up loggers -logger = ft_logger.Logger("security_scan").getLogger() -paramiko.util.log_to_file("/var/log/paramiko.log") - - -class SetUp: - def __init__(self, *args): - self.args = args - - def keystonepass(self): - com = self.args[0] - client = paramiko.SSHClient() - privatekeyfile = os.path.expanduser('/root/.ssh/id_rsa') - selectedkey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - client.connect(INSTALLER_IP, port=22, username='stack', - pkey=selectedkey) - except paramiko.SSHException: - logger.error("Password is invalid for " - "undercloud host: {0}".format(INSTALLER_IP)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "undercloud host: {0}".format(INSTALLER_IP)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(INSTALLER_IP)) - stdin, stdout, stderr = client.exec_command(com) - return stdout.read() - client.close() - - def getockey(self): - remotekey = self.args[0] - localkey = self.args[1] - privatekeyfile = os.path.expanduser('/root/.ssh/id_rsa') - selectedkey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - transport = paramiko.Transport((INSTALLER_IP, 22)) - transport.connect(username='stack', pkey=selectedkey) - try: - sftp = paramiko.SFTPClient.from_transport(transport) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(INSTALLER_IP)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(INSTALLER_IP)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(INSTALLER_IP)) - sftp.get(remotekey, localkey) - sftp.close() - transport.close() - - -class ConnectionManager: - def __init__(self, host, port, user, localkey, *args): - self.host = host - self.port = port - self.user = user - self.localkey = localkey - self.args = args - - def remotescript(self): - localpath = self.args[0] - remotepath = self.args[1] - com = self.args[2] - - client = paramiko.SSHClient() - privatekeyfile = os.path.expanduser('/root/.ssh/id_rsa') - selectedkey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Connection to undercloud - try: - client.connect(INSTALLER_IP, port=22, username='stack', - pkey=selectedkey) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(self.host)) - - transport = client.get_transport() - local_addr = ('127.0.0.1', 0) - channel = transport.open_channel("direct-tcpip", - (self.host, int(self.port)), - (local_addr)) - remote_client = paramiko.SSHClient() - remote_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Tunnel to overcloud - try: - remote_client.connect('127.0.0.1', port=22, username=self.user, - key_filename=self.localkey, sock=channel) - sftp = remote_client.open_sftp() - sftp.put(localpath, remotepath) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(self.host)) - - output = "" - stdin, stdout, stderr = remote_client.exec_command(com) - stdout = stdout.readlines() - # remove script - sftp.remove(remotepath) - remote_client.close() - client.close() - # Pipe back stout - for line in stdout: - output = output + line - if output != "": - return output - - def remotecmd(self): - com = self.args[0] - - client = paramiko.SSHClient() - privatekeyfile = os.path.expanduser('/root/.ssh/id_rsa') - selectedkey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Connection to undercloud - try: - client.connect(INSTALLER_IP, port=22, username='stack', - pkey=selectedkey) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(self.host)) - - transport = client.get_transport() - local_addr = ('127.0.0.1', 0) # 0 denotes choose random port - channel = transport.open_channel("direct-tcpip", - (self.host, int(self.port)), - (local_addr)) - remote_client = paramiko.SSHClient() - remote_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Tunnel to overcloud - try: - remote_client.connect('127.0.0.1', port=22, username=self.user, - key_filename=self.localkey, sock=channel) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(self.host)) - - chan = remote_client.get_transport().open_session() - chan.get_pty() - feed = chan.makefile() - chan.exec_command(com) - print feed.read() - - remote_client.close() - client.close() - - def download_reports(self): - dl_folder = self.args[0] - reportfile = self.args[1] - reportname = self.args[2] - resultsname = self.args[3] - client = paramiko.SSHClient() - privatekeyfile = os.path.expanduser('/root/.ssh/id_rsa') - selectedkey = paramiko.RSAKey.from_private_key_file(privatekeyfile) - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Connection to overcloud - try: - client.connect(INSTALLER_IP, port=22, username='stack', - pkey=selectedkey) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(self.host)) - - transport = client.get_transport() - local_addr = ('127.0.0.1', 0) # 0 denotes choose random port - channel = transport.open_channel("direct-tcpip", - (self.host, int(self.port)), - (local_addr)) - remote_client = paramiko.SSHClient() - remote_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Tunnel to overcloud - try: - remote_client.connect('127.0.0.1', port=22, username=self.user, - key_filename=self.localkey, sock=channel) - except paramiko.SSHException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except paramiko.AuthenticationException: - logger.error("Authentication failed for " - "host: {0}".format(self.host)) - except socket.error: - logger.error("Socker Connection failed for " - "undercloud host: {0}".format(self.host)) - # Download the reports - sftp = remote_client.open_sftp() - logger.info("Downloading \"{0}\"...".format(reportname)) - sftp.get(reportfile, ('{0}/{1}'.format(dl_folder, reportname))) - logger.info("Downloading \"{0}\"...".format(resultsname)) - sftp.get(reportfile, ('{0}/{1}'.format(dl_folder, resultsname))) - sftp.close() - transport.close() diff --git a/functest/opnfv_tests/security_scan/examples/xccdf-rhel7-server-upstream.ini b/functest/opnfv_tests/security_scan/examples/xccdf-rhel7-server-upstream.ini deleted file mode 100644 index 43b2e82d..00000000 --- a/functest/opnfv_tests/security_scan/examples/xccdf-rhel7-server-upstream.ini +++ /dev/null @@ -1,29 +0,0 @@ -[undercloud] -port = 22 -user = stack -remotekey = /home/stack/.ssh/id_rsa -localkey = /root/.ssh/overCloudKey - -[controller] -port = 22 -user = heat-admin -scantype = xccdf -secpolicy = /usr/share/xml/scap/ssg/content/ssg-centos7-xccdf.xml -cpe = /usr/share/xml/scap/ssg/content/ssg-rhel7-cpe-dictionary.xml -profile = stig-rhel7-server-upstream -report = report.hmtl -results = results.xml -reports_dir=/home/opnfv/functest/results/security_scan/ -clean = True - -[compute] -port = 22 -user = heat-admin -scantype = xccdf -secpolicy = /usr/share/xml/scap/ssg/content/ssg-centos7-xccdf.xml -cpe = /usr/share/xml/scap/ssg/content/ssg-rhel7-cpe-dictionary.xml -profile = stig-rhel7-server-upstream -report = report.hmtl -results = results.xml -reports_dir=/home/opnfv/functest/results/security_scan/ -clean = True diff --git a/functest/opnfv_tests/security_scan/examples/xccdf-standard.ini b/functest/opnfv_tests/security_scan/examples/xccdf-standard.ini deleted file mode 100644 index bfbcf82d..00000000 --- a/functest/opnfv_tests/security_scan/examples/xccdf-standard.ini +++ /dev/null @@ -1,29 +0,0 @@ -[undercloud] -port = 22 -user = stack -remotekey = /home/stack/.ssh/id_rsa -localkey = /root/.ssh/overCloudKey - -[controller] -port = 22 -user = heat-admin -scantype = xccdf -secpolicy = /usr/share/xml/scap/ssg/content/ssg-centos7-xccdf.xml -cpe = /usr/share/xml/scap/ssg/content/ssg-rhel7-cpe-dictionary.xml -profile = standard -report = report.hmtl -results = results.xml -reports_dir=/home/opnfv/functest/results/security_scan/ -clean = True - -[compute] -port = 22 -user = heat-admin -scantype = xccdf -secpolicy = /usr/share/xml/scap/ssg/content/ssg-centos7-xccdf.xml -cpe = /usr/share/xml/scap/ssg/content/ssg-rhel7-cpe-dictionary.xml -profile = standard -report = report.hmtl -results = results.xml -reports_dir=/home/opnfv/functest/results/security_scan/ -clean = True diff --git a/functest/opnfv_tests/security_scan/scripts/createfiles.py b/functest/opnfv_tests/security_scan/scripts/createfiles.py deleted file mode 100644 index b828901a..00000000 --- a/functest/opnfv_tests/security_scan/scripts/createfiles.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2016 Red Hat -# Luke Hinds (lhinds@redhat.com) -# 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 -# -# 0.1: This script creates the needed local files into a tmp directory. Should -# '--clean' be passed, all files will be removed, post scan. - - -import os -import tempfile - -files = ['results.xml', 'report.html', 'syschar.xml'] - - -directory_name = tempfile.mkdtemp() - -for i in files: - os.system("touch %s/%s" % (directory_name, i)) - -print directory_name diff --git a/functest/opnfv_tests/security_scan/scripts/internet_check.py b/functest/opnfv_tests/security_scan/scripts/internet_check.py deleted file mode 100644 index d417d174..00000000 --- a/functest/opnfv_tests/security_scan/scripts/internet_check.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2016 Red Hat -# Luke Hinds (lhinds@redhat.com) -# 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 -# -# Performs simple connection check, falls to default timeout of 10 seconds - -import socket - -TEST_HOST = "google.com" - - -def is_connected(): - try: - host = socket.gethostbyname(TEST_HOST) - socket.create_connection((host, 80), 2) - return True - except: - return False - - -print is_connected() diff --git a/functest/opnfv_tests/security_scan/security_scan.py b/functest/opnfv_tests/security_scan/security_scan.py deleted file mode 100755 index f0673924..00000000 --- a/functest/opnfv_tests/security_scan/security_scan.py +++ /dev/null @@ -1,220 +0,0 @@ -#!/usr/bin/python -# -# Copyright (c) 2016 Red Hat -# Luke Hinds (lhinds@redhat.com) -# 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 -# -# 0.1: This script installs OpenSCAP on the remote host, and scans the -# nominated node. Post scan a report is downloaded and if '--clean' is passed -# all trace of the scan is removed from the remote system. - - -import datetime -import os -import sys -from ConfigParser import SafeConfigParser - -import argparse -from keystoneclient import session -from keystoneclient.auth.identity import v2 -from novaclient import client - -import connect -import functest.utils.functest_constants as ft_constants - -__version__ = 0.1 -__author__ = 'Luke Hinds (lhinds@redhat.com)' -__url__ = 'https://wiki.opnfv.org/display/functest/Functest+Security' - -# Global vars -INSTALLER_IP = ft_constants.CI_INSTALLER_IP -oscapbin = 'sudo /bin/oscap' -functest_dir = '%s/security_scan/' % ft_constants.FUNCTEST_TEST_DIR - -# Apex Spefic var needed to query Undercloud -if ft_constants.OS_AUTH_URL is None: - connect.logger.error(" Enviroment variable OS_AUTH_URL is not set") - sys.exit(0) -else: - OS_AUTH_URL = ft_constants.OS_AUTH_URL - -# args -parser = argparse.ArgumentParser(description='OPNFV OpenSCAP Scanner') -parser.add_argument('--config', action='store', dest='cfgfile', - help='Config file', required=True) -args = parser.parse_args() - -# Config Parser -cfgparse = SafeConfigParser() -cfgparse.read(args.cfgfile) - -# Grab Undercloud key -remotekey = cfgparse.get('undercloud', 'remotekey') -localkey = cfgparse.get('undercloud', 'localkey') -setup = connect.SetUp(remotekey, localkey) -setup.getockey() - - -# Configure Nova Credentials -com = 'sudo /usr/bin/hiera admin_password' -setup = connect.SetUp(com) -keypass = setup.keystonepass() -auth = v2.Password(auth_url=OS_AUTH_URL, - username='admin', - password=str(keypass).rstrip(), - tenant_name='admin') -sess = session.Session(auth=auth) -nova = client.Client(2, session=sess) - - -class GlobalVariables: - tmpdir = "" - - -def run_tests(host, nodetype): - user = cfgparse.get(nodetype, 'user') - port = cfgparse.get(nodetype, 'port') - connect.logger.info("Host: {0} Selected Profile: {1}".format(host, - nodetype)) - connect.logger.info("Checking internet for package installation...") - if internet_check(host, nodetype): - connect.logger.info("Internet Connection OK.") - connect.logger.info("Creating temp file structure..") - createfiles(host, port, user, localkey) - connect.logger.debug("Installing OpenSCAP...") - install_pkg(host, port, user, localkey) - connect.logger.debug("Running scan...") - run_scanner(host, port, user, localkey, nodetype) - clean = cfgparse.get(nodetype, 'clean') - connect.logger.info("Post installation tasks....") - post_tasks(host, port, user, localkey, nodetype) - if clean: - connect.logger.info("Cleaning down environment....") - connect.logger.debug("Removing OpenSCAP....") - removepkg(host, port, user, localkey, nodetype) - connect.logger.info("Deleting tmp file and reports (remote)...") - cleandir(host, port, user, localkey, nodetype) - else: - connect.logger.error("Internet timeout. Moving on to next node..") - pass - - -def nova_iterate(): - # Find compute nodes, active with network on ctlplane - for server in nova.servers.list(): - if server.status == 'ACTIVE' and 'compute' in server.name: - networks = server.networks - nodetype = 'compute' - for host in networks['ctlplane']: - run_tests(host, nodetype) - # Find controller nodes, active with network on ctlplane - elif server.status == 'ACTIVE' and 'controller' in server.name: - networks = server.networks - nodetype = 'controller' - for host in networks['ctlplane']: - run_tests(host, nodetype) - - -def internet_check(host, nodetype): - import connect - user = cfgparse.get(nodetype, 'user') - port = cfgparse.get(nodetype, 'port') - localpath = functest_dir + 'scripts/internet_check.py' - remotepath = '/tmp/internet_check.py' - com = 'python /tmp/internet_check.py' - testconnect = connect.ConnectionManager(host, port, user, localkey, - localpath, remotepath, com) - connectionresult = testconnect.remotescript() - if connectionresult.rstrip() == 'True': - return True - else: - return False - - -def createfiles(host, port, user, localkey): - import connect - localpath = functest_dir + 'scripts/createfiles.py' - remotepath = '/tmp/createfiles.py' - com = 'python /tmp/createfiles.py' - connect = connect.ConnectionManager(host, port, user, localkey, - localpath, remotepath, com) - GlobalVariables.tmpdir = connect.remotescript() - - -def install_pkg(host, port, user, localkey): - import connect - com = 'sudo yum -y install openscap-scanner scap-security-guide' - connect = connect.ConnectionManager(host, port, user, localkey, com) - connect.remotecmd() - - -def run_scanner(host, port, user, localkey, nodetype): - import connect - scantype = cfgparse.get(nodetype, 'scantype') - profile = cfgparse.get(nodetype, 'profile') - results = cfgparse.get(nodetype, 'results') - report = cfgparse.get(nodetype, 'report') - secpolicy = cfgparse.get(nodetype, 'secpolicy') - # Here is where we contruct the actual scan command - if scantype == 'xccdf': - cpe = cfgparse.get(nodetype, 'cpe') - com = '{0} xccdf eval --profile {1} --results {2}/{3}' \ - ' --report {2}/{4}' \ - ' --cpe {5} {6}'.format(oscapbin, - profile, - GlobalVariables.tmpdir.rstrip(), - results, - report, - cpe, - secpolicy) - connect = connect.ConnectionManager(host, port, user, localkey, com) - connect.remotecmd() - elif scantype == 'oval': - com = '{0} oval eval --results {1}/{2} ' - '--report {1}/{3} {4}'.format(oscapbin, - GlobalVariables.tmpdir.rstrip(), - results, report, secpolicy) - connect = connect.ConnectionManager(host, port, user, localkey, com) - connect.remotecmd() - else: - com = '{0} oval-collect '.format(oscapbin) - connect = connect.ConnectionManager(host, port, user, localkey, com) - connect.remotecmd() - - -def post_tasks(host, port, user, localkey, nodetype): - import connect - # Create the download folder for functest dashboard and download reports - reports_dir = cfgparse.get(nodetype, 'reports_dir') - dl_folder = os.path.join(reports_dir, host + "_" + - datetime.datetime. - now().strftime('%Y-%m-%d_%H-%M-%S')) - os.makedirs(dl_folder, 0755) - report = cfgparse.get(nodetype, 'report') - results = cfgparse.get(nodetype, 'results') - reportfile = '{0}/{1}'.format(GlobalVariables.tmpdir.rstrip(), report) - connect = connect.ConnectionManager(host, port, user, localkey, dl_folder, - reportfile, report, results) - connect.download_reports() - - -def removepkg(host, port, user, localkey, nodetype): - import connect - com = 'sudo yum -y remove openscap-scanner scap-security-guide' - connect = connect.ConnectionManager(host, port, user, localkey, com) - connect.remotecmd() - - -def cleandir(host, port, user, localkey, nodetype): - import connect - com = 'sudo rm -r {0}'.format(GlobalVariables.tmpdir.rstrip()) - connect = connect.ConnectionManager(host, port, user, localkey, com) - connect.remotecmd() - - -if __name__ == '__main__': - nova_iterate() diff --git a/functest/opnfv_tests/vnf/ims/clearwater.py b/functest/opnfv_tests/vnf/ims/clearwater.py index eb0abacd..32c6dc5c 100644 --- a/functest/opnfv_tests/vnf/ims/clearwater.py +++ b/functest/opnfv_tests/vnf/ims/clearwater.py @@ -12,7 +12,7 @@ ######################################################################## -class Clearwater: +class Clearwater(object): def __init__(self, inputs={}, orchestrator=None, logger=None): self.config = inputs diff --git a/functest/opnfv_tests/vnf/ims/cloudify_ims.py b/functest/opnfv_tests/vnf/ims/cloudify_ims.py index 13a5af4f..c2c251ad 100644 --- a/functest/opnfv_tests/vnf/ims/cloudify_ims.py +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.py @@ -11,7 +11,9 @@ import json import os import requests import subprocess +import sys import time +import yaml import functest.core.vnf_base as vnf_base import functest.utils.functest_logger as ft_logger @@ -29,57 +31,127 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): repo='', cmd=''): super(ImsVnf, self).__init__(project, case, repo, cmd) self.logger = ft_logger.Logger("vIMS").getLogger() - self.case_dir = os.path.join(CONST.functest_test, 'vnf/ims/') - self.data_dir = CONST.dir_vIMS_data + self.case_dir = os.path.join(CONST.dir_functest_test, 'vnf/ims/') + self.data_dir = CONST.dir_ims_data self.test_dir = CONST.dir_repo_vims_test + # Retrieve the configuration + try: + self.config = CONST.__getattribute__( + 'vnf_{}_config'.format(self.case_name)) + except: + raise Exception("VNF config file not found") + + config_file = self.case_dir + self.config self.orchestrator = dict( - requirements=CONST.cloudify_requirements, - blueprint=CONST.cloudify_blueprint, - inputs=CONST.cloudify_inputs + requirements=get_config("cloudify.requirements", config_file), + blueprint=get_config("cloudify.blueprint", config_file), + inputs=get_config("cloudify.inputs", config_file) ) - + self.logger.debug("Orchestrator configuration: %s" % self.orchestrator) self.vnf = dict( - blueprint=CONST.clearwater_blueprint, - deployment_name=CONST.clearwater_deployment_name, - inputs=CONST.clearwater_inputs, - requirements=CONST.clearwater_requirements + blueprint=get_config("clearwater.blueprint", config_file), + deployment_name=get_config("clearwater.deployment_name", + config_file), + inputs=get_config("clearwater.inputs", config_file), + requirements=get_config("clearwater.requirements", config_file) ) + self.logger.debug("VNF configuration: %s" % self.vnf) + + self.images = get_config("tenant_images", config_file) + self.logger.info("Images needed for vIMS: %s" % self.images) # vIMS Data directory creation if not os.path.exists(self.data_dir): os.makedirs(self.data_dir) def deploy_orchestrator(self, **kwargs): + + self.logger.info("Additional pre-configuration steps") + self.neutron_client = os_utils.get_neutron_client(self.admin_creds) + self.glance_client = os_utils.get_glance_client(self.admin_creds) + self.keystone_client = os_utils.get_keystone_client(self.admin_creds) + self.nova_client = os_utils.get_nova_client(self.admin_creds) + + # needs some images + self.logger.info("Upload some OS images if it doesn't exist") + temp_dir = os.path.join(self.data_dir, "tmp/") + for image_name, image_url in self.images.iteritems(): + self.logger.info("image: %s, url: %s" % (image_name, image_url)) + try: + image_id = os_utils.get_image_id(self.glance_client, + image_name) + self.logger.debug("image_id: %s" % image_id) + except: + self.logger.error("Unexpected error: %s" % sys.exc_info()[0]) + + if image_id == '': + self.logger.info("""%s image doesn't exist on glance repository. Try + downloading this image and upload on glance !""" % image_name) + image_id = download_and_add_image_on_glance(self.glance_client, + image_name, + image_url, + temp_dir) + if image_id == '': + self.step_failure( + "Failed to find or upload required OS " + "image for this deployment") + # Need to extend quota + self.logger.info("Update security group quota for this tenant") + tenant_id = os_utils.get_tenant_id(self.keystone_client, + self.tenant_name) + self.logger.debug("Tenant id found %s" % tenant_id) + if not os_utils.update_sg_quota(self.neutron_client, + tenant_id, 50, 100): + self.step_failure("Failed to update security group quota" + + " for tenant " + self.tenant_name) + self.logger.debug("group quota extended") + + # start the deployment of cloudify public_auth_url = os_utils.get_endpoint('identity') - cfy = Orchestrator(self.data_dir, self.orchestrator.inputs) - self.orchestrator.object = cfy + self.logger.debug("CFY inputs: %s" % self.orchestrator['inputs']) + cfy = Orchestrator(self.data_dir, self.orchestrator['inputs']) + self.orchestrator['object'] = cfy + self.logger.debug("Orchestrator object created") - if 'tenant_name' in self.creds.keys(): - tenant_name = self.creds['tenant_name'] - elif 'project_name' in self.creds.keys(): - tenant_name = self.creds['project_name'] + self.logger.debug("Tenant name: %s" % self.tenant_name) - cfy.set_credentials(username=self.creds['username'], - password=self.creds['password'], - tenant_name=tenant_name, + cfy.set_credentials(username=self.tenant_name, + password=self.tenant_name, + tenant_name=self.tenant_name, auth_url=public_auth_url) + self.logger.info("Credentials set in CFY") # orchestrator VM flavor - flavor_id = self.get_flavor("m1.large", self.orchestrator.requirements) + self.logger.info("Check Flavor is available, if not create one") + self.logger.debug("Flavor details %s " % + self.orchestrator['requirements']['ram_min']) + flavor_exist, flavor_id = os_utils.get_or_create_flavor( + "m1.large", + self.orchestrator['requirements']['ram_min'], + '50', + '2', + public=True) + self.logger.debug("Flavor id: %s" % flavor_id) + if not flavor_id: self.logger.info("Available flavors are: ") - self.pMsg(self.nova_client.flavor.list()) + self.logger.info(self.nova_client.flavor.list()) self.step_failure("Failed to find required flavor" "for this deployment") cfy.set_flavor_id(flavor_id) + self.logger.debug("Flavor OK") # orchestrator VM image - if 'os_image' in self.orchestrator.requirements.keys(): + self.logger.debug("Orchestrator image") + if 'os_image' in self.orchestrator['requirements'].keys(): image_id = os_utils.get_image_id( - self.glance_client, self.orchestrator.requirements['os_image']) + self.glance_client, + self.orchestrator['requirements']['os_image']) + self.logger.debug("Orchestrator image id: %s" % image_id) if image_id == '': + self.logger.error("CFY image not found") self.step_failure("Failed to find required OS image" " for cloudify manager") else: @@ -87,21 +159,22 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): " for cloudify manager") cfy.set_image_id(image_id) + self.logger.debug("Orchestrator image set") + self.logger.debug("Get External network") ext_net = os_utils.get_external_net(self.neutron_client) + self.logger.debug("External network: %s" % ext_net) if not ext_net: self.step_failure("Failed to get external network") cfy.set_external_network_name(ext_net) + self.logger.debug("CFY External network set") + self.logger.debug("get resolvconf") ns = ft_utils.get_resolvconf_ns() if ns: cfy.set_nameservers(ns) - - if 'compute' in self.nova_client.client.services_url: - cfy.set_nova_url(self.nova_client.client.services_url['compute']) - if self.neutron_client.httpclient.endpoint_url is not None: - cfy.set_neutron_url(self.neutron_client.httpclient.endpoint_url) + self.logger.debug("Resolvconf set") self.logger.info("Prepare virtualenv for cloudify-cli") cmd = "chmod +x " + self.case_dir + "create_venv.sh" @@ -110,30 +183,42 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): cmd = self.case_dir + "create_venv.sh " + self.data_dir ft_utils.execute_command(cmd) - cfy.download_manager_blueprint(self.orchestrator.blueprint['url'], - self.orchestrator.blueprint['branch']) + cfy.download_manager_blueprint( + self.orchestrator['blueprint']['url'], + self.orchestrator['blueprint']['branch']) - cfy.deploy_manager() - return {'status': 'PASS', 'result': ''} + error = cfy.deploy_manager() + if error: + self.logger.error(error) + return {'status': 'FAIL', 'result': error} + else: + return {'status': 'PASS', 'result': ''} def deploy_vnf(self): - cw = Clearwater(self.vnf.inputs, self.orchestrator.object, self.logger) - self.vnf.object = cw + cw = Clearwater(self.vnf['inputs'], self.orchestrator['object'], + self.logger) + self.vnf['object'] = cw self.logger.info("Collect flavor id for all clearwater vm") - flavor_id = self.get_flavor("m1.small", self.vnf.requirements) + flavor_exist, flavor_id = os_utils.get_or_create_flavor( + "m1.small", + self.vnf['requirements']['ram_min'], + '20', + '1', + public=True) + self.logger.debug("Flavor id: %s" % flavor_id) if not flavor_id: self.logger.info("Available flavors are: ") - self.pMsg(self.nova_client.flavor.list()) + self.logger.info(self.nova_client.flavor.list()) self.step_failure("Failed to find required flavor" " for this deployment") cw.set_flavor_id(flavor_id) # VMs image - if 'os_image' in self.vnf.requirements.keys(): + if 'os_image' in self.vnf['requirements'].keys(): image_id = os_utils.get_image_id( - self.glance_client, self.vnf.requirements['os_image']) + self.glance_client, self.vnf['requirements']['os_image']) if image_id == '': self.step_failure("Failed to find required OS image" " for clearwater VMs") @@ -149,8 +234,12 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): cw.set_external_network_name(ext_net) - cw.deploy_vnf() - return {'status': 'PASS', 'result': ''} + error = cw.deploy_vnf() + if error: + self.logger.error(error) + return {'status': 'FAIL', 'result': error} + else: + return {'status': 'PASS', 'result': ''} def test_vnf(self): script = "source {0}venv_cloudify/bin/activate; " @@ -168,7 +257,7 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): api_url = "http://" + mgr_ip + "/api/v2" dep_outputs = requests.get(api_url + "/deployments/" + - self.vnf.deployment_name + "/outputs") + self.vnf['deployment_name'] + "/outputs") dns_ip = dep_outputs.json()['outputs']['dns_ip'] ellis_ip = dep_outputs.json()['outputs']['ellis_ip'] @@ -252,27 +341,58 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): return {'status': 'FAIL', 'result': ''} def clean(self): - self.vnf.object.undeploy_vnf() - self.orchestrator.object.undeploy_manager() + self.vnf['object'].undeploy_vnf() + self.orchestrator['object'].undeploy_manager() super(ImsVnf, self).clean() - def get_flavor(self, flavor_name, requirements): - try: - flavor_id = os_utils.get_flavor_id(self.nova_client, flavor_name) - if 'ram_min' in requirements.keys(): - flavor_id = os_utils.get_flavor_id_by_ram_range( - self.nova_client, requirements['ram_min'], 7500) - - if flavor_id == '': - self.logger.error( - "Failed to find %s flavor. " - "Try with ram range default requirement !" % flavor_name) - flavor_id = os_utils.get_flavor_id_by_ram_range( - self.nova_client, - 4000, 10000) - return flavor_id - except: - self.logger.error("Flavor '%s' not found." % self.flavor_name) - self.logger.info("Available flavors are: ") - self.pMsg(self.nova_client.flavor.list()) - return None + def main(self, **kwargs): + self.logger.info("Cloudify IMS VNF onboarding test starting") + self.execute() + self.logger.info("Cloudify IMS VNF onboarding test executed") + if self.criteria is "PASS": + return self.EX_OK + else: + return self.EX_RUN_ERROR + + def run(self): + kwargs = {} + return self.main(**kwargs) + + +# ---------------------------------------------------------- +# +# YAML UTILS +# +# ----------------------------------------------------------- +def get_config(parameter, file): + """ + Returns the value of a given parameter in file.yaml + parameter must be given in string format with dots + Example: general.openstack.image_name + """ + with open(file) as f: + file_yaml = yaml.safe_load(f) + f.close() + value = file_yaml + for element in parameter.split("."): + value = value.get(element) + if value is None: + raise ValueError("The parameter %s is not defined in" + " reporting.yaml" % parameter) + return value + + +def download_and_add_image_on_glance(glance, image_name, image_url, data_dir): + dest_path = data_dir + if not os.path.exists(dest_path): + os.makedirs(dest_path) + file_name = image_url.rsplit('/')[-1] + if not ft_utils.download_url(image_url, dest_path): + return False + + image = os_utils.create_glance_image( + glance, image_name, dest_path + file_name) + if not image: + return False + + return image diff --git a/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml b/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml new file mode 100644 index 00000000..775685fa --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.yaml @@ -0,0 +1,39 @@ +tenant_images: + ubuntu_14.04: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img + centos_7: http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1510.qcow2 +cloudify: + blueprint: + url: https://github.com/boucherv-orange/cloudify-manager-blueprints.git + branch: '3.3.1-build' + requirements: + ram_min: 4000 + 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 + requirements: + ram_min: 2000 + os_image: ubuntu_14.04 + inputs: + image_id: '' + flavor_id: '' + agent_user: ubuntu + external_network_name: '' + public_domain: clearwaterfv diff --git a/functest/opnfv_tests/vnf/ims/opera_ims.py b/functest/opnfv_tests/vnf/ims/opera_ims.py index 073a56c3..7ead401f 100644 --- a/functest/opnfv_tests/vnf/ims/opera_ims.py +++ b/functest/opnfv_tests/vnf/ims/opera_ims.py @@ -8,148 +8,393 @@ # http://www.apache.org/licenses/LICENSE-2.0 import json -import os -import requests -import subprocess +import socket +import sys import time +import yaml import functest.core.vnf_base as vnf_base 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 os from functest.utils.constants import CONST +from org.openbaton.cli.agents.agents import MainAgent +from org.openbaton.cli.errors.errors import NfvoException + + +def servertest(host, port): + args = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM) + for family, socktype, proto, canonname, sockaddr in args: + s = socket.socket(family, socktype, proto) + try: + s.connect(sockaddr) + except socket.error: + return False + else: + s.close() + return True + +# ---------------------------------------------------------- +# +# UTILS +# +# ----------------------------------------------------------- + + +def get_config(parameter, file): + """ + Returns the value of a given parameter in file.yaml + parameter must be given in string format with dots + Example: general.openstack.image_name + """ + with open(file) as f: + file_yaml = yaml.safe_load(f) + f.close() + value = file_yaml + for element in parameter.split("."): + value = value.get(element) + if value is None: + raise ValueError("The parameter %s is not defined in" + " reporting.yaml" % parameter) + return value + + +def download_and_add_image_on_glance(glance, image_name, + image_url, data_dir): + dest_path = data_dir + if not os.path.exists(dest_path): + os.makedirs(dest_path) + file_name = image_url.rsplit('/')[-1] + if not ft_utils.download_url(image_url, dest_path): + return False + image = os_utils.create_glance_image( + glance, image_name, dest_path + file_name) + if not image: + return False + return image + class ImsVnf(vnf_base.VnfOnBoardingBase): - def __init__(self, project='functest', case='opera_ims', + def __init__(self, project='functest', case='orchestra_ims', repo='', cmd=''): super(ImsVnf, self).__init__(project, case, repo, cmd) - self.logger = ft_logger.Logger("vIMS").getLogger() - self.case_dir = os.path.join(CONST.functest_test, 'vnf/ims/') - self.data_dir = CONST.dir_vIMS_data + self.ob_password = "openbaton" + self.ob_username = "admin" + self.ob_https = False + self.ob_port = "8080" + self.ob_ip = "localhost" + self.ob_instance_id = "" + self.logger = ft_logger.Logger("orchestra_ims").getLogger() + self.case_dir = os.path.join(CONST.dir_functest_test, 'vnf/ims/') + self.data_dir = CONST.dir_ims_data self.test_dir = CONST.dir_repo_vims_test - + self.ob_projectid = "" + self.keystone_client = os_utils.get_keystone_client() + self.ob_nsr_id = "" + self.main_agent = None # vIMS Data directory creation if not os.path.exists(self.data_dir): os.makedirs(self.data_dir) + # Retrieve the configuration + try: + self.config = CONST.__getattribute__( + 'vnf_{}_config'.format(self.case_name)) + except: + raise Exception("Orchestra VNF config file not found") + config_file = self.case_dir + self.config + self.imagename = get_config("openbaton.imagename", config_file) + self.market_link = get_config("openbaton.marketplace_link", + config_file) + self.images = get_config("tenant_images", config_file) def deploy_orchestrator(self, **kwargs): - # TODO - # deploy open-O from Functest docker located on the Jumphost - # you have admin rights on OpenStack SUT - # you can cretae a VM, spawn docker on the jumphost - # spawn docker on a VM in the SUT, ..up to you - # - # note: this step can be ignored - # if Open-O is part of the installer + self.logger.info("Additional pre-configuration steps") + nova_client = os_utils.get_nova_client() + neutron_client = os_utils.get_neutron_client() + glance_client = os_utils.get_glance_client() + + # needs some images + self.logger.info("Upload some OS images if it doesn't exist") + temp_dir = os.path.join(self.data_dir, "tmp/") + for image_name, image_url in self.images.iteritems(): + self.logger.info("image: %s, url: %s" % (image_name, image_url)) + try: + image_id = os_utils.get_image_id(glance_client, + image_name) + self.logger.info("image_id: %s" % image_id) + except: + self.logger.error("Unexpected error: %s" % sys.exc_info()[0]) + + if image_id == '': + self.logger.info("""%s image doesn't exist on glance repository. Try + downloading this image and upload on glance !""" % image_name) + image_id = download_and_add_image_on_glance(glance_client, + image_name, + image_url, + temp_dir) + if image_id == '': + self.step_failure( + "Failed to find or upload required OS " + "image for this deployment") + network_dic = os_utils.create_network_full(neutron_client, + "openbaton_mgmt", + "openbaton_mgmt_subnet", + "openbaton_router", + "192.168.100.0/24") + + # orchestrator VM flavor + self.logger.info("Check medium Flavor is available, if not create one") + flavor_exist, flavor_id = os_utils.get_or_create_flavor( + "m1.medium", + "4096", + '1', + '2', + public=True) + self.logger.debug("Flavor id: %s" % flavor_id) + + if not network_dic: + self.logger.error("There has been a problem when creating the " + "neutron network") + + network_id = network_dic["net_id"] + + self.logger.info("Creating floating IP for VM in advance...") + floatip_dic = os_utils.create_floating_ip(neutron_client) + floatip = floatip_dic['fip_addr'] + + if floatip is None: + self.logger.error("Cannot create floating IP.") + + userdata = "#!/bin/bash\n" + userdata += "set -x\n" + userdata += "set -e\n" + userdata += "echo \"nameserver 8.8.8.8\" >> /etc/resolv.conf\n" + userdata += "apt-get install curl\n" + userdata += ("echo \"rabbitmq_broker_ip=%s\" > ./config_file\n" + % floatip) + userdata += "echo \"mysql=no\" >> ./config_file\n" + userdata += ("echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuPXrV3" + "geeHc6QUdyUr/1Z+yQiqLcOskiEGBiXr4z76MK4abiFmDZ18OMQlc" + "fl0p3kS0WynVgyaOHwZkgy/DIoIplONVr2CKBKHtPK+Qcme2PVnCtv" + "EqItl/FcD+1h5XSQGoa+A1TSGgCod/DPo+pes0piLVXP8Ph6QS1k7S" + "ic7JDeRQ4oT1bXYpJ2eWBDMfxIWKZqcZRiGPgMIbJ1iEkxbpeaAd9O" + "4MiM9nGCPESmed+p54uYFjwEDlAJZShcAZziiZYAvMZhvAhe6USljc" + "7YAdalAnyD/jwCHuwIrUw/lxo7UdNCmaUxeobEYyyFA1YVXzpNFZya" + "XPGAAYIJwEq/ openbaton@opnfv\" >> /home/ubuntu/.ssh/aut" + "horized_keys\n") + userdata += "cat ./config_file\n" + userdata += ("curl -s http://get.openbaton.org/bootstrap " + "> ./bootstrap\n") + userdata += "export OPENBATON_COMPONENT_AUTOSTART=false\n" + bootstrap = "sh ./bootstrap release -configFile=./config_file" + userdata += bootstrap + "\n" + + userdata += ("echo \"nfvo.plugin.timeout=300000\" >> " + "/etc/openbaton/openbaton-nfvo.properties\n") + userdata += "service openbaton-nfvo restart\n" + userdata += "service openbaton-vnfm-generic restart\n" + + sg_id = os_utils.create_security_group_full(neutron_client, + "orchestra-sec-group", + "allowall") + + os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress", + "icmp", 0, 255) + os_utils.create_secgroup_rule(neutron_client, sg_id, "egress", + "icmp", 0, 255) + os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress", + "tcp", 1, 65535) + os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress", + "udp", 1, 65535) + os_utils.create_secgroup_rule(neutron_client, sg_id, "egress", + "tcp", 1, 65535) + os_utils.create_secgroup_rule(neutron_client, sg_id, "egress", + "udp", 1, 65535) + + self.logger.info("Security group set") + + self.logger.info("Create instance....") + self.logger.info("flavor: m1.medium\n" + "image: %s\n" + "network_id: %s\n" + "userdata: %s\n" + % (self.imagename, network_id, userdata)) + + instance = os_utils.create_instance_and_wait_for_active( + "m1.medium", + os_utils.get_image_id(glance_client, self.imagename), + network_id, + "orchestra-openbaton", + config_drive=False, + userdata=userdata) + + self.ob_instance_id = instance.id + + self.logger.info("Adding sec group to orchestra instance") + os_utils.add_secgroup_to_instance(nova_client, + self.ob_instance_id, sg_id) + + self.logger.info("Associating floating ip: '%s' to VM '%s' " + % (floatip, "orchestra-openbaton")) + if not os_utils.add_floating_ip(nova_client, instance.id, floatip): + self.logger.error("Cannot associate floating IP to VM.") + self.step_failure("Cannot associate floating IP to VM.") + + self.logger.info("Waiting for nfvo to be up and running...") + x = 0 + while x < 100: + if servertest(floatip, "8080"): + break + else: + self.logger.debug("openbaton is not started yet") + time.sleep(5) + x += 1 + + if x == 100: + self.logger.error("Openbaton is not started correctly") + self.step_failure("Openbaton is not started correctly") + + self.ob_ip = floatip + self.ob_password = "openbaton" + self.ob_username = "admin" + self.ob_https = False + self.ob_port = "8080" + self.logger.info("Deploy orchestrator: OK") def deploy_vnf(self): - # TODO - self.logger.info("Deploy VNF: OK") + self.logger.info("vIMS Deployment") + + self.main_agent = MainAgent(nfvo_ip=self.ob_ip, + nfvo_port=self.ob_port, + https=self.ob_https, + version=1, + username=self.ob_username, + password=self.ob_password) + + project_agent = self.main_agent.get_agent("project", self.ob_projectid) + for p in json.loads(project_agent.find()): + if p.get("name") == "default": + self.ob_projectid = p.get("id") + break + + self.logger.debug("project id: %s" % self.ob_projectid) + if self.ob_projectid == "": + self.logger.error("Default project id was not found!") + self.step_failure("Default project id was not found!") + + vim_json = { + "name": "vim-instance", + "authUrl": os_utils.get_credentials().get("auth_url"), + "tenant": os_utils.get_credentials().get("tenant_name"), + "username": os_utils.get_credentials().get("username"), + "password": os_utils.get_credentials().get("password"), + # "keyPair": "opnfv", + # TODO change the keypair to correct value + # or upload a correct one or remove it + "securityGroups": [ + "default", + "orchestra-sec-group" + ], + "type": "openstack", + "location": { + "name": "opnfv", + "latitude": "52.525876", + "longitude": "13.314400" + } + } + + self.logger.debug("vim: %s" % vim_json) + + self.main_agent.get_agent( + "vim", + project_id=self.ob_projectid).create(entity=json.dumps(vim_json)) + + market_agent = self.main_agent.get_agent("market", + project_id=self.ob_projectid) + + nsd = {} + try: + self.logger.info("sending: %s" % self.market_link) + nsd = market_agent.create(entity=self.market_link) + self.logger.info("Onboarded nsd: " + nsd.get("name")) + except NfvoException as e: + self.step_failure(e.message) + + nsr_agent = self.main_agent.get_agent("nsr", + project_id=self.ob_projectid) + nsd_id = nsd.get('id') + if nsd_id is None: + self.step_failure("NSD not onboarded correctly") + + nsr = None + try: + nsr = nsr_agent.create(nsd_id) + except NfvoException as e: + self.step_failure(e.message) + + if nsr.get('code') is not None: + self.logger.error( + "vIMS cannot be deployed: %s -> %s" % + (nsr.get('code'), nsr.get('message'))) + self.step_failure("vIMS cannot be deployed") + + i = 0 + self.logger.info("waiting NSR to go to active...") + while nsr.get("status") != 'ACTIVE' and nsr.get("status") != 'ERROR': + i += 1 + if i == 100: + self.step_failure("After %s sec the nsr did not go to active.." + % 5 * 100) + time.sleep(5) + nsr = json.loads(nsr_agent.find(nsr.get('id'))) + + if nsr.get("status") == 'ACTIVE': + deploy_vnf = {'status': "PASS", 'result': nsr} + self.logger.info("Deploy VNF: OK") + else: + deploy_vnf = {'status': "FAIL", 'result': nsr} + self.logger.error("Deploy VNF: ERROR") + self.step_failure("Deploy vIMS failed") + self.ob_nsr_id = nsr.get("id") + return deploy_vnf def test_vnf(self): # Adaptations probably needed # code used for cloudify_ims # ruby client on jumphost calling the vIMS on the SUT - script = "source {0}venv_cloudify/bin/activate; " - script += "cd {0}; " - script += "cfy status | grep -Eo \"([0-9]{{1,3}}\.){{3}}[0-9]{{1,3}}\"" - cmd = "/bin/bash -c '" + script.format(self.data_dir) + "'" + return - try: - self.logger.debug("Trying to get clearwater manager IP ... ") - mgr_ip = os.popen(cmd).read() - mgr_ip = mgr_ip.splitlines()[0] - except: - self.step_failure("Unable to retrieve the IP of the " - "cloudify manager server !") - - api_url = "http://" + mgr_ip + "/api/v2" - dep_outputs = requests.get(api_url + "/deployments/" + - self.vnf.deployment_name + "/outputs") - dns_ip = dep_outputs.json()['outputs']['dns_ip'] - ellis_ip = dep_outputs.json()['outputs']['ellis_ip'] - - ellis_url = "http://" + ellis_ip + "/" - url = ellis_url + "accounts" - - params = {"password": "functest", - "full_name": "opnfv functest user", - "email": "functest@opnfv.fr", - "signup_code": "secret"} - - rq = requests.post(url, data=params) - i = 20 - while rq.status_code != 201 and i > 0: - rq = requests.post(url, data=params) - i = i - 1 - time.sleep(10) - - if rq.status_code == 201: - url = ellis_url + "session" - rq = requests.post(url, data=params) - cookies = rq.cookies - - url = ellis_url + "accounts/" + params['email'] + "/numbers" - if cookies != "": - rq = requests.post(url, cookies=cookies) - i = 24 - while rq.status_code != 200 and i > 0: - rq = requests.post(url, cookies=cookies) - i = i - 1 - time.sleep(25) - - if rq.status_code != 200: - self.step_failure("Unable to create a number: %s" - % rq.json()['reason']) - - nameservers = ft_utils.get_resolvconf_ns() - resolvconf = "" - for ns in nameservers: - resolvconf += "\nnameserver " + ns - - if dns_ip != "": - script = ('echo -e "nameserver ' + dns_ip + resolvconf + - '" > /etc/resolv.conf; ') - script += 'source /etc/profile.d/rvm.sh; ' - script += 'cd {0}; ' - script += ('rake test[{1}] SIGNUP_CODE="secret"') - - cmd = ("/bin/bash -c '" + - script.format(self.data_dir, self.inputs["public_domain"]) + - "'") - output_file = "output.txt" - f = open(output_file, 'w+') - subprocess.call(cmd, shell=True, stdout=f, - stderr=subprocess.STDOUT) - f.close() - - f = open(output_file, 'r') - result = f.read() - if result != "": - self.logger.debug(result) - - vims_test_result = "" - tempFile = os.path.join(self.test_dir, "temp.json") - try: - self.logger.debug("Trying to load test results") - with open(tempFile) as f: - vims_test_result = json.load(f) - f.close() - except: - self.logger.error("Unable to retrieve test results") + def clean(self): + self.main_agent.get_agent( + "nsr", + project_id=self.ob_projectid).delete(self.ob_nsr_id) + time.sleep(5) + os_utils.delete_instance(nova_client=os_utils.get_nova_client(), + instance_id=self.ob_instance_id) + # TODO question is the clean removing also the VM? + # I think so since is goinf to remove the tenant... + super(ImsVnf, self).clean() - try: - os.remove(tempFile) - except: - self.logger.error("Deleting file failed") + def main(self, **kwargs): + self.logger.info("Orchestra IMS VNF onboarding test starting") + self.execute() + self.logger.info("Orchestra IMS VNF onboarding test executed") + if self.criteria is "PASS": + return self.EX_OK + else: + return self.EX_RUN_ERROR - if vims_test_result != '': - return {'status': 'PASS', 'result': vims_test_result} - else: - return {'status': 'FAIL', 'result': ''} + def run(self): + kwargs = {} + return self.main(**kwargs) - def clean(self): - # TODO - super(ImsVnf, self).clean() + +if __name__ == '__main__': + test = ImsVnf() + test.deploy_orchestrator() + test.deploy_vnf() + test.clean() diff --git a/functest/opnfv_tests/vnf/ims/orchestra_ims.py b/functest/opnfv_tests/vnf/ims/orchestra_ims.py index 28f37f05..352b609b 100644 --- a/functest/opnfv_tests/vnf/ims/orchestra_ims.py +++ b/functest/opnfv_tests/vnf/ims/orchestra_ims.py @@ -8,150 +8,383 @@ # http://www.apache.org/licenses/LICENSE-2.0 import json -import os -import requests -import subprocess +import socket +import sys import time +import yaml import functest.core.vnf_base as vnf_base 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 os from functest.utils.constants import CONST +from org.openbaton.cli.agents.agents import MainAgent +from org.openbaton.cli.errors.errors import NfvoException -class ImsVnf(vnf_base.VnfOnBoardingBase): +def servertest(host, port): + args = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM) + for family, socktype, proto, canonname, sockaddr in args: + s = socket.socket(family, socktype, proto) + try: + s.connect(sockaddr) + except socket.error: + return False + else: + s.close() + return True + + +class ImsVnf(vnf_base.VnfOnBoardingBase): def __init__(self, project='functest', case='orchestra_ims', repo='', cmd=''): super(ImsVnf, self).__init__(project, case, repo, cmd) - self.logger = ft_logger.Logger("vIMS").getLogger() - self.case_dir = os.path.join(CONST.functest_test, 'vnf/ims/') - self.data_dir = CONST.dir_vIMS_data + self.ob_password = "openbaton" + self.ob_username = "admin" + self.ob_https = False + self.ob_port = "8080" + self.ob_ip = "localhost" + self.ob_instance_id = "" + self.logger = ft_logger.Logger("orchestra_ims").getLogger() + self.case_dir = os.path.join(CONST.dir_functest_test, 'vnf/ims/') + self.data_dir = CONST.dir_ims_data self.test_dir = CONST.dir_repo_vims_test - + self.ob_projectid = "" + self.keystone_client = os_utils.get_keystone_client() + self.ob_nsr_id = "" + self.main_agent = None # vIMS Data directory creation if not os.path.exists(self.data_dir): os.makedirs(self.data_dir) + # Retrieve the configuration + try: + self.config = CONST.__getattribute__( + 'vnf_{}_config'.format(self.case_name)) + except: + raise Exception("Orchestra VNF config file not found") + config_file = self.case_dir + self.config + self.imagename = get_config("openbaton.imagename", config_file) + self.market_link = get_config("openbaton.marketplace_link", + config_file) + self.images = get_config("tenant_images", config_file) def deploy_orchestrator(self, **kwargs): - # TODO - # put your code here to deploy openbaton - # from the functest docker located on the jumphost - # you have admin rights on OpenStack SUT - # you can cretae a VM, spawn docker on the jumphost - # spawn docker on a VM in the SUT, ..up to you - # - # note: this step can be ignored - # if OpenBaton is part of the installer + self.logger.info("Additional pre-configuration steps") + nova_client = os_utils.get_nova_client() + neutron_client = os_utils.get_neutron_client() + glance_client = os_utils.get_glance_client() + + # Import images if needed + self.logger.info("Upload some OS images if it doesn't exist") + temp_dir = os.path.join(self.data_dir, "tmp/") + for image_name, image_url in self.images.iteritems(): + self.logger.info("image: %s, url: %s" % (image_name, image_url)) + try: + image_id = os_utils.get_image_id(glance_client, + image_name) + self.logger.info("image_id: %s" % image_id) + except: + self.logger.error("Unexpected error: %s" % sys.exc_info()[0]) + + if image_id == '': + self.logger.info("""%s image doesn't exist on glance repository. Try + downloading this image and upload on glance !""" % image_name) + image_id = download_and_add_image_on_glance(glance_client, + image_name, + image_url, + temp_dir) + if image_id == '': + self.step_failure( + "Failed to find or upload required OS " + "image for this deployment") + network_dic = os_utils.create_network_full(neutron_client, + "openbaton_mgmt", + "openbaton_mgmt_subnet", + "openbaton_router", + "192.168.100.0/24") + + # orchestrator VM flavor + self.logger.info("Check medium Flavor is available, if not create one") + flavor_exist, flavor_id = os_utils.get_or_create_flavor( + "m1.medium", + "4096", + '20', + '2', + public=True) + self.logger.debug("Flavor id: %s" % flavor_id) + + if not network_dic: + self.logger.error("There has been a problem when creating the " + "neutron network") + + network_id = network_dic["net_id"] + + self.logger.info("Creating floating IP for VM in advance...") + floatip_dic = os_utils.create_floating_ip(neutron_client) + floatip = floatip_dic['fip_addr'] + + if floatip is None: + self.logger.error("Cannot create floating IP.") + + userdata = "#!/bin/bash\n" + userdata += "set -x\n" + userdata += "set -e\n" + userdata += "echo \"nameserver 8.8.8.8\" >> /etc/resolv.conf\n" + userdata += "apt-get install curl\n" + userdata += ("echo \"rabbitmq_broker_ip=%s\" > ./config_file\n" + % floatip) + userdata += "echo \"mysql=no\" >> ./config_file\n" + userdata += ("echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuPXrV3" + "geeHc6QUdyUr/1Z+yQiqLcOskiEGBiXr4z76MK4abiFmDZ18OMQlc" + "fl0p3kS0WynVgyaOHwZkgy/DIoIplONVr2CKBKHtPK+Qcme2PVnCtv" + "EqItl/FcD+1h5XSQGoa+A1TSGgCod/DPo+pes0piLVXP8Ph6QS1k7S" + "ic7JDeRQ4oT1bXYpJ2eWBDMfxIWKZqcZRiGPgMIbJ1iEkxbpeaAd9O" + "4MiM9nGCPESmed+p54uYFjwEDlAJZShcAZziiZYAvMZhvAhe6USljc" + "7YAdalAnyD/jwCHuwIrUw/lxo7UdNCmaUxeobEYyyFA1YVXzpNFZya" + "XPGAAYIJwEq/ openbaton@opnfv\" >> /home/ubuntu/.ssh/aut" + "horized_keys\n") + userdata += "cat ./config_file\n" + userdata += ("curl -s http://get.openbaton.org/bootstrap " + "> ./bootstrap\n") + userdata += "export OPENBATON_COMPONENT_AUTOSTART=false\n" + bootstrap = "sh ./bootstrap release -configFile=./config_file" + userdata += bootstrap + "\n" + + userdata += ("echo \"nfvo.plugin.timeout=300000\" >> " + "/etc/openbaton/openbaton-nfvo.properties\n") + userdata += "service openbaton-nfvo restart\n" + userdata += "service openbaton-vnfm-generic restart\n" + + sg_id = os_utils.create_security_group_full(neutron_client, + "orchestra-sec-group", + "allowall") + + os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress", + "icmp", 0, 255) + os_utils.create_secgroup_rule(neutron_client, sg_id, "egress", + "icmp", 0, 255) + os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress", + "tcp", 1, 65535) + os_utils.create_secgroup_rule(neutron_client, sg_id, "ingress", + "udp", 1, 65535) + os_utils.create_secgroup_rule(neutron_client, sg_id, "egress", + "tcp", 1, 65535) + os_utils.create_secgroup_rule(neutron_client, sg_id, "egress", + "udp", 1, 65535) + + self.logger.info("Security group set") + + self.logger.info("Create instance....") + self.logger.info("flavor: m1.medium\n" + "image: %s\n" + "network_id: %s\n" + "userdata: %s\n" + % (self.imagename, network_id, userdata)) + + instance = os_utils.create_instance_and_wait_for_active( + "m1.medium", + os_utils.get_image_id(glance_client, self.imagename), + network_id, + "orchestra-openbaton", + config_drive=False, + userdata=userdata) + + self.ob_instance_id = instance.id + + self.logger.info("Adding sec group to orchestra instance") + os_utils.add_secgroup_to_instance(nova_client, + self.ob_instance_id, sg_id) + + self.logger.info("Associating floating ip: '%s' to VM '%s' " + % (floatip, "orchestra-openbaton")) + if not os_utils.add_floating_ip(nova_client, instance.id, floatip): + self.logger.error("Cannot associate floating IP to VM.") + self.step_failure("Cannot associate floating IP to VM.") + + self.logger.info("Waiting for nfvo to be up and running...") + x = 0 + while x < 100: + if servertest(floatip, "8080"): + break + else: + self.logger.debug("openbaton is not started yet") + time.sleep(5) + x += 1 + + if x == 100: + self.logger.error("Openbaton is not started correctly") + self.step_failure("Openbaton is not started correctly") + + self.ob_ip = floatip + self.ob_password = "openbaton" + self.ob_username = "admin" + self.ob_https = False + self.ob_port = "8080" + self.logger.info("Deploy orchestrator: OK") def deploy_vnf(self): - # deploy the VNF - # call openbaton to deploy the vIMS + self.logger.info("vIMS Deployment") + + self.main_agent = MainAgent(nfvo_ip=self.ob_ip, + nfvo_port=self.ob_port, + https=self.ob_https, + version=1, + username=self.ob_username, + password=self.ob_password) + + project_agent = self.main_agent.get_agent("project", self.ob_projectid) + for p in json.loads(project_agent.find()): + if p.get("name") == "default": + self.ob_projectid = p.get("id") + break + + self.logger.debug("project id: %s" % self.ob_projectid) + if self.ob_projectid == "": + self.logger.error("Default project id was not found!") + self.step_failure("Default project id was not found!") + + vim_json = { + "name": "vim-instance", + "authUrl": os_utils.get_credentials().get("auth_url"), + "tenant": os_utils.get_credentials().get("tenant_name"), + "username": os_utils.get_credentials().get("username"), + "password": os_utils.get_credentials().get("password"), + "keyPair": "opnfv", + # TODO change the keypair to correct value + # or upload a correct one or remove it + "securityGroups": [ + "default", + "orchestra-sec-group" + ], + "type": "openstack", + "location": { + "name": "opnfv", + "latitude": "52.525876", + "longitude": "13.314400" + } + } + + self.logger.debug("vim: %s" % vim_json) + + self.main_agent.get_agent( + "vim", + project_id=self.ob_projectid).create(entity=json.dumps(vim_json)) + + market_agent = self.main_agent.get_agent("market", + project_id=self.ob_projectid) + + nsd = {} + try: + self.logger.info("sending: %s" % self.market_link) + nsd = market_agent.create(entity=self.market_link) + self.logger.info("Onboarded nsd: " + nsd.get("name")) + except NfvoException as e: + self.step_failure(e.message) + + nsr_agent = self.main_agent.get_agent("nsr", + project_id=self.ob_projectid) + nsd_id = nsd.get('id') + if nsd_id is None: + self.step_failure("NSD not onboarded correctly") + + nsr = None + try: + nsr = nsr_agent.create(nsd_id) + except NfvoException as e: + self.step_failure(e.message) + + if nsr is None: + self.step_failure("NSR not deployed correctly") + + i = 0 + self.logger.info("waiting NSR to go to active...") + while nsr.get("status") != 'ACTIVE': + i += 1 + if i == 100: + self.step_failure("After %s sec the nsr did not go to active.." + % 5 * 100) + time.sleep(5) + nsr = json.loads(nsr_agent.find(nsr.get('id'))) + + deploy_vnf = {'status': "PASS", 'result': nsr} + self.ob_nsr_id = nsr.get("id") self.logger.info("Deploy VNF: OK") + return deploy_vnf def test_vnf(self): # Adaptations probably needed # code used for cloudify_ims # ruby client on jumphost calling the vIMS on the SUT - script = "source {0}venv_cloudify/bin/activate; " - script += "cd {0}; " - script += "cfy status | grep -Eo \"([0-9]{{1,3}}\.){{3}}[0-9]{{1,3}}\"" - cmd = "/bin/bash -c '" + script.format(self.data_dir) + "'" + return - try: - self.logger.debug("Trying to get clearwater manager IP ... ") - mgr_ip = os.popen(cmd).read() - mgr_ip = mgr_ip.splitlines()[0] - except: - self.step_failure("Unable to retrieve the IP of the " - "cloudify manager server !") - - api_url = "http://" + mgr_ip + "/api/v2" - dep_outputs = requests.get(api_url + "/deployments/" + - self.vnf.deployment_name + "/outputs") - dns_ip = dep_outputs.json()['outputs']['dns_ip'] - ellis_ip = dep_outputs.json()['outputs']['ellis_ip'] - - ellis_url = "http://" + ellis_ip + "/" - url = ellis_url + "accounts" - - params = {"password": "functest", - "full_name": "opnfv functest user", - "email": "functest@opnfv.fr", - "signup_code": "secret"} - - rq = requests.post(url, data=params) - i = 20 - while rq.status_code != 201 and i > 0: - rq = requests.post(url, data=params) - i = i - 1 - time.sleep(10) - - if rq.status_code == 201: - url = ellis_url + "session" - rq = requests.post(url, data=params) - cookies = rq.cookies - - url = ellis_url + "accounts/" + params['email'] + "/numbers" - if cookies != "": - rq = requests.post(url, cookies=cookies) - i = 24 - while rq.status_code != 200 and i > 0: - rq = requests.post(url, cookies=cookies) - i = i - 1 - time.sleep(25) - - if rq.status_code != 200: - self.step_failure("Unable to create a number: %s" - % rq.json()['reason']) - - nameservers = ft_utils.get_resolvconf_ns() - resolvconf = "" - for ns in nameservers: - resolvconf += "\nnameserver " + ns - - if dns_ip != "": - script = ('echo -e "nameserver ' + dns_ip + resolvconf + - '" > /etc/resolv.conf; ') - script += 'source /etc/profile.d/rvm.sh; ' - script += 'cd {0}; ' - script += ('rake test[{1}] SIGNUP_CODE="secret"') - - cmd = ("/bin/bash -c '" + - script.format(self.data_dir, self.inputs["public_domain"]) + - "'") - output_file = "output.txt" - f = open(output_file, 'w+') - subprocess.call(cmd, shell=True, stdout=f, - stderr=subprocess.STDOUT) - f.close() - - f = open(output_file, 'r') - result = f.read() - if result != "": - self.logger.debug(result) - - vims_test_result = "" - tempFile = os.path.join(self.test_dir, "temp.json") - try: - self.logger.debug("Trying to load test results") - with open(tempFile) as f: - vims_test_result = json.load(f) - f.close() - except: - self.logger.error("Unable to retrieve test results") + def clean(self): + self.main_agent.get_agent( + "nsr", + project_id=self.ob_projectid).delete(self.ob_nsr_id) + time.sleep(5) + os_utils.delete_instance(nova_client=os_utils.get_nova_client(), + instance_id=self.ob_instance_id) + # TODO question is the clean removing also the VM? + # I think so since is goinf to remove the tenant... + super(ImsVnf, self).clean() - try: - os.remove(tempFile) - except: - self.logger.error("Deleting file failed") + def main(self, **kwargs): + self.logger.info("Orchestra IMS VNF onboarding test starting") + self.execute() + self.logger.info("Orchestra IMS VNF onboarding test executed") + if self.criteria is "PASS": + return self.EX_OK + else: + return self.EX_RUN_ERROR - if vims_test_result != '': - return {'status': 'PASS', 'result': vims_test_result} - else: - return {'status': 'FAIL', 'result': ''} + def run(self): + kwargs = {} + return self.main(**kwargs) - def clean(self): - # TODO - super(ImsVnf, self).clean() + +if __name__ == '__main__': + test = ImsVnf() + test.deploy_orchestrator() + test.deploy_vnf() + test.clean() + + +# ---------------------------------------------------------- +# +# UTILS +# +# ----------------------------------------------------------- +def get_config(parameter, file): + """ + Returns the value of a given parameter in file.yaml + parameter must be given in string format with dots + Example: general.openstack.image_name + """ + with open(file) as f: + file_yaml = yaml.safe_load(f) + f.close() + value = file_yaml + for element in parameter.split("."): + value = value.get(element) + if value is None: + raise ValueError("The parameter %s is not defined in" + " reporting.yaml" % parameter) + return value + + +def download_and_add_image_on_glance(glance, image_name, + image_url, data_dir): + dest_path = data_dir + if not os.path.exists(dest_path): + os.makedirs(dest_path) + file_name = image_url.rsplit('/')[-1] + if not ft_utils.download_url(image_url, dest_path): + return False + image = os_utils.create_glance_image( + glance, image_name, dest_path + file_name) + if not image: + return False + return image diff --git a/functest/opnfv_tests/vnf/ims/orchestra_ims.yaml b/functest/opnfv_tests/vnf/ims/orchestra_ims.yaml new file mode 100644 index 00000000..2fb33df5 --- /dev/null +++ b/functest/opnfv_tests/vnf/ims/orchestra_ims.yaml @@ -0,0 +1,7 @@ +tenant_images: + ubuntu_14.04: http://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img + openims: http://marketplace.openbaton.org:8082/api/v1/images/52e2ccc0-1dce-4663-894d-28aab49323aa/img +openbaton: + bootstrap: sh <(curl -s http://get.openbaton.org/bootstrap) release -configFile= + marketplace_link: http://marketplace.openbaton.org:8082/api/v1/nsds/fokus/OpenImsCore/3.2.0/json + imagename: ubuntu_14.04 diff --git a/functest/opnfv_tests/vnf/ims/orchestrator_cloudify.py b/functest/opnfv_tests/vnf/ims/orchestrator_cloudify.py index f3838f87..82a9dca0 100644 --- a/functest/opnfv_tests/vnf/ims/orchestrator_cloudify.py +++ b/functest/opnfv_tests/vnf/ims/orchestrator_cloudify.py @@ -21,7 +21,7 @@ from git import Repo import functest.utils.functest_logger as ft_logger -class Orchestrator: +class Orchestrator(object): def __init__(self, testcase_dir, inputs={}): self.testcase_dir = testcase_dir @@ -114,6 +114,7 @@ class Orchestrator: cmd = "/bin/bash -c '" + script + "'" error = execute_command(cmd, self.logger) if error: + self.logger.error("Failed to deploy cloudify-manager") return error self.logger.info("Cloudify-manager server is UP !") @@ -171,6 +172,7 @@ class Orchestrator: cmd = "/bin/bash -c '" + script + "'" error = execute_command(cmd, self.logger, 2000) if error: + self.logger.error("Failed to deploy blueprint") return error self.logger.info("The deployment of {0} is ended".format(dep_name)) @@ -228,7 +230,4 @@ def execute_command(cmd, logger, timeout=1800): logger.error("Error when executing command %s" % cmd) f = open(output_file, 'r') lines = f.readlines() - result = lines[len(lines) - 3] - result += lines[len(lines) - 2] - result += lines[len(lines) - 1] - return result + return lines[-5:] diff --git a/functest/opnfv_tests/vnf/router/__init__.py b/functest/opnfv_tests/vnf/router/__init__.py new file mode 100755 index 00000000..e69de29b --- /dev/null +++ b/functest/opnfv_tests/vnf/router/__init__.py diff --git a/functest/opnfv_tests/vnf/router/vyos_vrouter.py b/functest/opnfv_tests/vnf/router/vyos_vrouter.py new file mode 100755 index 00000000..94a3ecfd --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vyos_vrouter.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# +# Copyright 2017 Okinawa Open Laboratory +# +# 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 +import json +import os + +RESULT_DETAILS_FILE = "test_result.json" + + +class VrouterVnf(base.FeatureBase): + def __init__(self): + super(VrouterVnf, self).__init__(project='vRouter', + case='vyos_vrouter', + repo='dir_repo_vrouter') + self.cmd = 'cd %s && ./run.sh' % self.repo + + def set_result_details(self): + filepath = os.path.join(self.repo, RESULT_DETAILS_FILE) + if os.path.exists(filepath): + f = open(filepath, 'r') + self.details = json.load(f) + f.close() + + def log_results(self): + if self.criteria == 'PASS': + self.set_result_details() + super(VrouterVnf, self).log_results() diff --git a/functest/tests/unit/core/test_testcase_base.py b/functest/tests/unit/core/test_testcase_base.py index 94d2e966..b7c81d87 100755..100644 --- a/functest/tests/unit/core/test_testcase_base.py +++ b/functest/tests/unit/core/test_testcase_base.py @@ -9,7 +9,6 @@ import logging import mock -import os import unittest from functest.core import testcase_base @@ -32,12 +31,11 @@ class TestcaseBaseTesting(unittest.TestCase): self.assertEqual(self.test.run(), testcase_base.TestcaseBase.EX_RUN_ERROR) - @mock.patch.dict(os.environ, {}) @mock.patch('functest.utils.functest_utils.push_results_to_db', return_value=False) def _test_missing_attribute(self, mock_function): - self.assertEqual(self.test.publish_report(), - testcase_base.TestcaseBase.EX_PUBLISH_RESULT_FAILED) + self.assertEqual(self.test.push_to_db(), + testcase_base.TestcaseBase.EX_PUSH_TO_DB_ERROR) mock_function.assert_not_called() def test_missing_case_name(self): @@ -70,7 +68,7 @@ class TestcaseBaseTesting(unittest.TestCase): return_value=False) def test_push_to_db_failed(self, mock_function): self.assertEqual(self.test.push_to_db(), - testcase_base.TestcaseBase.EX_PUBLISH_RESULT_FAILED) + testcase_base.TestcaseBase.EX_PUSH_TO_DB_ERROR) mock_function.assert_called_once_with( self.test.project, self.test.case_name, self.test.start_time, self.test.stop_time, self.test.criteria, self.test.details) diff --git a/functest/tests/unit/opnfv_tests/vnf/__init__.py b/functest/tests/unit/opnfv_tests/vnf/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/functest/tests/unit/opnfv_tests/vnf/__init__.py diff --git a/functest/tests/unit/opnfv_tests/vnf/ims/__init__.py b/functest/tests/unit/opnfv_tests/vnf/ims/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/functest/tests/unit/opnfv_tests/vnf/ims/__init__.py diff --git a/functest/tests/unit/opnfv_tests/vnf/ims/test_clearwater.py b/functest/tests/unit/opnfv_tests/vnf/ims/test_clearwater.py new file mode 100644 index 00000000..527f12e5 --- /dev/null +++ b/functest/tests/unit/opnfv_tests/vnf/ims/test_clearwater.py @@ -0,0 +1,54 @@ +#!/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.opnfv_tests.vnf.ims import clearwater +from functest.opnfv_tests.vnf.ims import orchestrator_cloudify + + +class ClearwaterTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.clearwater = clearwater.Clearwater() + self.orchestrator = orchestrator_cloudify.Orchestrator('test_dir') + self.clearwater.orchestrator = self.orchestrator + self.clearwater.dep_name = 'test_dep_name' + self.bp = {'file_name': 'test_file', + 'destination_folder': 'test_folder', + 'url': 'test_url', + 'branch': 'test_branch'} + + def test_deploy_vnf_blueprint_download_failed(self): + with mock.patch.object(self.clearwater.orchestrator, + 'download_upload_and_deploy_blueprint', + return_value='error'): + self.assertEqual(self.clearwater.deploy_vnf(self.bp), + 'error') + + def test_deploy_vnf_blueprint_download_passed(self): + with mock.patch.object(self.clearwater.orchestrator, + 'download_upload_and_deploy_blueprint', + return_value=''): + self.clearwater.deploy_vnf(self.bp), + self.assertEqual(self.clearwater.deploy, True) + + def test_undeploy_vnf_deployment_passed(self): + with mock.patch.object(self.clearwater.orchestrator, + 'undeploy_deployment'): + self.clearwater.deploy = True + self.clearwater.undeploy_vnf(), + self.assertEqual(self.clearwater.deploy, False) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/opnfv_tests/vnf/ims/test_cloudify_ims.py b/functest/tests/unit/opnfv_tests/vnf/ims/test_cloudify_ims.py new file mode 100644 index 00000000..e25816f0 --- /dev/null +++ b/functest/tests/unit/opnfv_tests/vnf/ims/test_cloudify_ims.py @@ -0,0 +1,542 @@ +#!/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.opnfv_tests.vnf.ims import cloudify_ims + + +class ImsVnfTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.makedirs'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'get_config', return_value='config_value'): + self.ims_vnf = cloudify_ims.ImsVnf() + self.neutron_client = mock.Mock() + self.glance_client = mock.Mock() + self.keystone_client = mock.Mock() + self.nova_client = mock.Mock() + self.orchestrator = {'requirements': {'ram_min': 2, + 'os_image': 'test_os_image'}, + 'blueprint': {'url': 'test_url', + 'branch': 'test_branch'}, + 'inputs': {'public_domain': 'test_domain'}, + 'object': 'test_object', + 'deployment_name': 'test_deployment_name'} + self.ims_vnf.orchestrator = self.orchestrator + self.ims_vnf.images = {'test_image': 'test_url'} + self.ims_vnf.vnf = self.orchestrator + self.ims_vnf.tenant_name = 'test_tenant' + self.ims_vnf.inputs = {'public_domain': 'test_domain'} + self.ims_vnf.glance_client = self.glance_client + self.ims_vnf.neutron_client = self.neutron_client + self.ims_vnf.keystone_client = self.keystone_client + self.ims_vnf.nova_client = self.nova_client + self.ims_vnf.admin_creds = 'test_creds' + + self.mock_post = mock.Mock() + attrs = {'status_code': 201, + 'cookies': ""} + self.mock_post.configure_mock(**attrs) + + self.mock_post_200 = mock.Mock() + attrs = {'status_code': 200, + 'cookies': ""} + self.mock_post_200.configure_mock(**attrs) + + def test_deploy_orchestrator_missing_image(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value=''), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'download_and_add_image_on_glance') as m, \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_orchestrator() + self.assertTrue(m.called) + msg = "Failed to find or upload required OS " + msg += "image for this deployment" + self.assertTrue(msg in context.exception) + + def test_deploy_orchestrator_extend_quota_fail(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_tenant_id', + return_value='tenant_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.update_sg_quota', + return_value=False), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_orchestrator() + msg = "Failed to update security group quota" + msg += " for tenant test_tenant" + self.assertTrue(msg in context.exception) + + def _get_image_id(self, client, name): + if name == 'test_image': + return 'image_id' + else: + return '' + + def test_deploy_orchestrator_missing_flavor(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + side_effect=self._get_image_id), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_tenant_id', + return_value='tenant_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.update_sg_quota', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_endpoint', + return_value='public_auth_url'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Orchestrator', return_value=mock.Mock()) as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(False, '')), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_orchestrator() + self.assertTrue(m.set_credentials.called) + msg = "Failed to find required flavorfor this deployment" + self.assertTrue(msg in context.exception) + + def test_deploy_orchestrator_missing_os_image(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + side_effect=self._get_image_id), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_tenant_id', + return_value='tenant_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.update_sg_quota', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_endpoint', + return_value='public_auth_url'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Orchestrator', return_value=mock.Mock()) as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'flavor_id')), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_orchestrator() + self.assertTrue(m.set_credentials.called) + self.assertTrue(m.set_flavor_id.called) + msg = "Failed to find required OS image for cloudify manager" + self.assertTrue(msg in context.exception) + + def test_deploy_orchestrator_get_ext_network_fail(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_tenant_id', + return_value='tenant_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.update_sg_quota', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_endpoint', + return_value='public_auth_url'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Orchestrator', return_value=mock.Mock()) as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'flavor_id')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_external_net', + return_value=''), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_orchestrator() + self.assertTrue(m.set_credentials.called) + self.assertTrue(m.set_flavor_id.called) + self.assertTrue(m.set_image_id.called) + msg = "Failed to get external network" + self.assertTrue(msg in context.exception) + + def test_deploy_orchestrator_with_error(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_tenant_id', + return_value='tenant_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.update_sg_quota', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_endpoint', + return_value='public_auth_url'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Orchestrator') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'flavor_id')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_external_net', + return_value='ext_net'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.get_resolvconf_ns', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.execute_command'): + mock_obj = mock.Mock() + attrs = {'deploy_manager.return_value': 'error'} + mock_obj.configure_mock(**attrs) + + m.return_value = mock_obj + + self.assertEqual(self.ims_vnf.deploy_orchestrator(), + {'status': 'FAIL', 'result': 'error'}) + + def test_deploy_orchestrator_default(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_neutron_client', + return_value=self.neutron_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_glance_client', + return_value=self.glance_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_keystone_client', + return_value=self.keystone_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_nova_client', + return_value=self.nova_client), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_tenant_id', + return_value='tenant_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.update_sg_quota', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_endpoint', + return_value='public_auth_url'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Orchestrator') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'flavor_id')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_external_net', + return_value='ext_net'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.get_resolvconf_ns', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.execute_command'): + mock_obj = mock.Mock() + attrs = {'deploy_manager.return_value': ''} + mock_obj.configure_mock(**attrs) + + m.return_value = mock_obj + + self.assertEqual(self.ims_vnf.deploy_orchestrator(), + {'status': 'PASS', 'result': ''}) + + def test_deploy_vnf_missing_flavor(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Clearwater', return_value=mock.Mock()), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(False, '')), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_vnf() + msg = "Failed to find required flavor for this deployment" + self.assertTrue(msg in context.exception) + + def test_deploy_vnf_missing_os_image(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Clearwater', return_value=mock.Mock()) as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'test_flavor')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value=''), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_vnf() + msg = "Failed to find required OS image" + msg += " for clearwater VMs" + self.assertTrue(msg in context.exception) + self.assertTrue(m.set_flavor_id.called) + + def test_deploy_vnf_missing_get_ext_net(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Clearwater', return_value=mock.Mock()) as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'test_flavor')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_external_net', + return_value=''), \ + self.assertRaises(Exception) as context: + self.ims_vnf.deploy_vnf() + msg = "Failed to get external network" + self.assertTrue(msg in context.exception) + self.assertTrue(m.set_flavor_id.called) + self.assertTrue(m.set_image_id.called) + + def test_deploy_vnf_with_error(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Clearwater') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'test_flavor')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_external_net', + return_value='ext_net'): + mock_obj = mock.Mock() + attrs = {'deploy_vnf.return_value': 'error'} + mock_obj.configure_mock(**attrs) + + m.return_value = mock_obj + + self.assertEqual(self.ims_vnf.deploy_vnf(), + {'status': 'FAIL', 'result': 'error'}) + + def test_deploy_vnf_default(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'Clearwater') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_or_create_flavor', + return_value=(True, 'test_flavor')), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_image_id', + return_value='image_id'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.get_external_net', + return_value='ext_net'): + mock_obj = mock.Mock() + attrs = {'deploy_vnf.return_value': ''} + mock_obj.configure_mock(**attrs) + + m.return_value = mock_obj + + self.assertEqual(self.ims_vnf.deploy_vnf(), + {'status': 'PASS', 'result': ''}) + + def test_test_vnf_ip_retrieval_failure(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.popen', side_effect=Exception), \ + self.assertRaises(Exception) as context: + msg = "Unable to retrieve the IP of the " + msg += "cloudify manager server !" + self.ims_vnf.test_vnf() + self.assertTrue(msg in context.exception) + + def test_test_vnf_create_number_failure(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.popen') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'requests.get'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'requests.post', + return_value=self.mock_post), \ + self.assertRaises(Exception) as context: + mock_obj = mock.Mock() + attrs = {'read.return_value': 'test_ip\n'} + mock_obj.configure_mock(**attrs) + m.return_value = mock_obj + + self.ims_vnf.test_vnf() + + msg = "Unable to create a number:" + self.assertTrue(msg in context.exception) + + def _get_post_status(self, url, cookies='', data=''): + ellis_url = "http://test_ellis_ip/session" + if url == ellis_url: + return self.mock_post_200 + return self.mock_post + + def test_test_vnf_fail(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.popen') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'requests.get') as mock_get, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'requests.post', + side_effect=self._get_post_status), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.get_resolvconf_ns'), \ + mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'subprocess.call'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.remove'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'json.load', return_value=''): + mock_obj = mock.Mock() + attrs = {'read.return_value': 'test_ip\n'} + mock_obj.configure_mock(**attrs) + m.return_value = mock_obj + + mock_obj2 = mock.Mock() + attrs = {'json.return_value': {'outputs': + {'dns_ip': 'test_dns_ip', + 'ellis_ip': 'test_ellis_ip'}}} + mock_obj2.configure_mock(**attrs) + mock_get.return_value = mock_obj2 + + self.assertEqual(self.ims_vnf.test_vnf(), + {'status': 'FAIL', 'result': ''}) + + def test_test_vnf_pass(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.popen') as m, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'requests.get') as mock_get, \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'requests.post', + side_effect=self._get_post_status), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.get_resolvconf_ns'), \ + mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'subprocess.call'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.remove'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'json.load', return_value='vims_test_result'): + mock_obj = mock.Mock() + attrs = {'read.return_value': 'test_ip\n'} + mock_obj.configure_mock(**attrs) + m.return_value = mock_obj + + mock_obj2 = mock.Mock() + attrs = {'json.return_value': {'outputs': + {'dns_ip': 'test_dns_ip', + 'ellis_ip': 'test_ellis_ip'}}} + mock_obj2.configure_mock(**attrs) + mock_get.return_value = mock_obj2 + + self.assertEqual(self.ims_vnf.test_vnf(), + {'status': 'PASS', 'result': 'vims_test_result'}) + + def test_download_and_add_image_on_glance_incorrect_url(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.makedirs'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.download_url', + return_value=False): + resp = cloudify_ims.download_and_add_image_on_glance(self. + glance_client, + 'image_name', + 'http://url', + 'data_dir') + self.assertEqual(resp, False) + + def test_download_and_add_image_on_glance_image_creation_failure(self): + with mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os.makedirs'), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'ft_utils.download_url', + return_value=True), \ + mock.patch('functest.opnfv_tests.vnf.ims.cloudify_ims.' + 'os_utils.create_glance_image', + return_value=''): + resp = cloudify_ims.download_and_add_image_on_glance(self. + glance_client, + 'image_name', + 'http://url', + 'data_dir') + self.assertEqual(resp, False) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/opnfv_tests/vnf/ims/test_orchestrator_cloudify.py b/functest/tests/unit/opnfv_tests/vnf/ims/test_orchestrator_cloudify.py new file mode 100644 index 00000000..620b0216 --- /dev/null +++ b/functest/tests/unit/opnfv_tests/vnf/ims/test_orchestrator_cloudify.py @@ -0,0 +1,122 @@ +#!/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.opnfv_tests.vnf.ims import orchestrator_cloudify + + +class ImsVnfTesting(unittest.TestCase): + + logging.disable(logging.CRITICAL) + + def setUp(self): + self.orchestrator = orchestrator_cloudify.Orchestrator('test_dir') + self.bp = {'file_name': 'test_file', + 'destination_folder': 'test_folder', + 'url': 'test_url', + 'branch': 'test_branch'} + + def test_download_manager_blueprint_download_blueprint_failed(self): + self.orchestrator.manager_blueprint = False + with mock.patch.object(self.orchestrator, '_download_blueprints', + return_value=False), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'exit') as mock_exit: + self.orchestrator.download_manager_blueprint('test_url', + 'test_branch') + mock_exit.assert_any_call(-1) + + def test_download_manager_blueprint_download_blueprint_passed(self): + self.orchestrator.manager_blueprint = False + with mock.patch.object(self.orchestrator, '_download_blueprints', + return_value=True): + self.orchestrator.download_manager_blueprint('test_url', + 'test_branch') + self.assertEqual(self.orchestrator.manager_blueprint, + True) + + def test_deploy_manager_failed(self): + self.orchestrator.manager_blueprint = True + with mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'os.remove'), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'execute_command', return_value='error'): + self.assertEqual(self.orchestrator.deploy_manager(), + 'error') + self.assertEqual(self.orchestrator.manager_up, + False) + + def test_deploy_manager_passed(self): + self.orchestrator.manager_blueprint = True + with mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'os.remove'), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'execute_command', return_value=''): + self.orchestrator.deploy_manager() + self.assertEqual(self.orchestrator.manager_up, + True) + + def test_undeploy_manager_passed(self): + with mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'execute_command', return_value=''): + self.orchestrator.deploy_manager() + self.assertEqual(self.orchestrator.manager_up, + False) + + def test_dwnld_upload_and_depl_blueprint_dwnld_blueprint_failed(self): + with mock.patch.object(self.orchestrator, '_download_blueprints', + return_value=False), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'exit', side_effect=Exception) as mock_exit, \ + self.assertRaises(Exception): + self.orchestrator.download_upload_and_deploy_blueprint(self.bp, + 'cfig', + 'bpn', + 'dpn') + mock_exit.assert_any_call(-1) + + def test_dwnld_upload_and_depl_blueprint_failed(self): + with mock.patch.object(self.orchestrator, '_download_blueprints', + return_value=True), \ + mock.patch('__builtin__.open', mock.mock_open()), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'execute_command', return_value='error'): + r = self.orchestrator.download_upload_and_deploy_blueprint(self.bp, + 'cfig', + 'bpn', + 'dpn') + self.assertEqual(r, 'error') + + def test__download_blueprints_failed(self): + with mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'shutil.rmtree'), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'Repo.clone_from', side_effect=Exception): + self.assertEqual(self.orchestrator._download_blueprints('bp_url', + 'branch', + 'dest'), + False) + + def test__download_blueprints_passed(self): + with mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'shutil.rmtree'), \ + mock.patch('functest.opnfv_tests.vnf.ims.orchestrator_cloudify.' + 'Repo.clone_from'): + self.assertEqual(self.orchestrator._download_blueprints('bp_url', + 'branch', + 'dest'), + True) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/functest/tests/unit/utils/test_openstack_utils.py b/functest/tests/unit/utils/test_openstack_utils.py index 0f510414..447271fc 100644 --- a/functest/tests/unit/utils/test_openstack_utils.py +++ b/functest/tests/unit/utils/test_openstack_utils.py @@ -7,6 +7,7 @@ import copy import logging +import os import unittest import mock @@ -353,18 +354,34 @@ class OSUtilsTesting(unittest.TestCase): def test_get_credentials_missing_endpoint_type(self): self._get_credentials_missing_env('OS_ENDPOINT_TYPE') + def _test_source_credentials(self, msg, key='OS_TENANT_NAME', + value='admin'): + try: + del os.environ[key] + except: + pass + f = 'rc_file' + with mock.patch('__builtin__.open', mock.mock_open(read_data=msg), + create=True) as m: + m.return_value.__iter__ = lambda self: iter(self.readline, '') + openstack_utils.source_credentials(f) + m.assert_called_once_with(f, 'r') + self.assertEqual(os.environ[key], value) + def test_source_credentials(self): - with mock.patch('functest.utils.openstack_utils.subprocess.Popen') \ - as mock_subproc_popen, \ - mock.patch('functest.utils.openstack_utils.os.environ'): - process_mock = mock.Mock() - attrs = {'communicate.return_value': ('OS_USER_NAME=test_name', - 'success')} - process_mock.configure_mock(**attrs) - mock_subproc_popen.return_value = process_mock - - self.assertDictEqual(openstack_utils.source_credentials('rc_file'), - {'OS_USER_NAME': 'test_name'}) + self._test_source_credentials('OS_TENANT_NAME=admin') + self._test_source_credentials('OS_TENANT_NAME= admin') + self._test_source_credentials('OS_TENANT_NAME = admin') + self._test_source_credentials('OS_TENANT_NAME = "admin"') + self._test_source_credentials('export OS_TENANT_NAME=admin') + self._test_source_credentials('export OS_TENANT_NAME =admin') + self._test_source_credentials('export OS_TENANT_NAME = admin') + self._test_source_credentials('export OS_TENANT_NAME = "admin"') + self._test_source_credentials('OS_TENANT_NAME', value='') + self._test_source_credentials('export OS_TENANT_NAME', value='') + # This test will fail as soon as rc_file is fixed + self._test_source_credentials( + 'export "\'OS_TENANT_NAME\'" = "\'admin\'"') @mock.patch('functest.utils.openstack_utils.os.getenv', return_value=None) diff --git a/functest/utils/decorators.py b/functest/utils/decorators.py new file mode 100644 index 00000000..99bcef3e --- /dev/null +++ b/functest/utils/decorators.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +import mock +import requests.sessions +import urlparse + + +def can_dump_request_to_file(method): + + def dump_preparedrequest(request, **kwargs): + parseresult = urlparse.urlparse(request.url) + if parseresult.scheme == "file": + with open(parseresult.path.replace('/results', ''), 'a') as f: + headers = "" + for key in request.headers: + headers += key + " " + request.headers[key] + "\n" + message = "{} {}\n{}\n{}\n\n\n".format( + request.method, request.url, headers, request.body) + f.write(message) + return mock.Mock() + + def patch_request(method, url, **kwargs): + with requests.sessions.Session() as session: + parseresult = urlparse.urlparse(url) + if parseresult.scheme == "file": + with mock.patch.object( + session, 'send', side_effect=dump_preparedrequest): + return session.request(method=method, url=url, **kwargs) + else: + return session.request(method=method, url=url, **kwargs) + + def hook(*args, **kwargs): + with mock.patch('requests.api.request', side_effect=patch_request): + return method(*args, **kwargs) + + return hook diff --git a/functest/utils/env.py b/functest/utils/env.py index fa5245fb..7e4df2ea 100644 --- a/functest/utils/env.py +++ b/functest/utils/env.py @@ -3,7 +3,7 @@ import re default_envs = { 'NODE_NAME': 'unknown_pod', - 'CI_DEBUG': 'true', + 'CI_DEBUG': 'false', 'DEPLOY_SCENARIO': 'os-nosdn-nofeature-noha', 'DEPLOY_TYPE': 'virt', 'INSTALLER_TYPE': None, diff --git a/functest/utils/functest_logger.py b/functest/utils/functest_logger.py index f09f56be..022211cb 100755 --- a/functest/utils/functest_logger.py +++ b/functest/utils/functest_logger.py @@ -28,42 +28,38 @@ import json from functest.utils.constants import CONST -logger = logging.getLogger(__name__) +class Logger(object): -def is_debug(): - if CONST.CI_DEBUG and CONST.CI_DEBUG.lower() == "true": - return True - return False - - -def setup_logging(default_path=CONST.dir_functest_logging_cfg, - default_level=logging.INFO, - env_key='LOG_CFG'): - path = default_path - value = os.getenv(env_key, None) - if value: - path = value - if os.path.exists(path): - with open(path, 'rt') as f: - config = json.load(f) - if (config['handlers'] and - config['handlers']['console']): - stream_level = logging.INFO - if is_debug(): - stream_level = logging.DEBUG - config['handlers']['console']['level'] = stream_level - logging.config.dictConfig(config) - else: - logging.basicConfig(level=default_level) - - -setup_logging() - - -class Logger: def __init__(self, logger_name): + self.setup_logging() self.logger = logging.getLogger(logger_name) + logging.getLogger("paramiko").setLevel(logging.WARNING) def getLogger(self): return self.logger + + def is_debug(self): + if CONST.CI_DEBUG and CONST.CI_DEBUG.lower() == "true": + return True + return False + + def setup_logging(self, default_path=CONST.dir_functest_logging_cfg, + default_level=logging.INFO, + env_key='LOG_CFG'): + path = default_path + value = os.getenv(env_key, None) + if value: + path = value + if os.path.exists(path): + with open(path, 'rt') as f: + config = json.load(f) + if (config['handlers'] and + config['handlers']['console']): + stream_level = logging.INFO + if self.is_debug(): + stream_level = logging.DEBUG + config['handlers']['console']['level'] = stream_level + logging.config.dictConfig(config) + else: + logging.basicConfig(level=default_level) diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py index 04055464..dbed811a 100644 --- a/functest/utils/functest_utils.py +++ b/functest/utils/functest_utils.py @@ -23,10 +23,9 @@ import requests import yaml from git import Repo -from functest.utils.constants import CONST +from functest.utils import decorators import functest.utils.functest_logger as ft_logger - logger = ft_logger.Logger("functest_utils").getLogger() @@ -184,43 +183,14 @@ def logger_test_results(project, case_name, status, details): 'd': details}) -def write_results_to_file(project, case_name, start_date, - stop_date, criteria, details): - file_path = re.split(r'://', CONST.results_test_db_url)[1] - - try: - installer = os.environ['INSTALLER_TYPE'] - scenario = os.environ['DEPLOY_SCENARIO'] - pod_name = os.environ['NODE_NAME'] - except KeyError as e: - logger.error("Please set env var: " + str(e)) - return False - - test_start = dt.fromtimestamp(start_date).strftime('%Y-%m-%d %H:%M:%S') - test_stop = dt.fromtimestamp(stop_date).strftime('%Y-%m-%d %H:%M:%S') - - params = {"project_name": project, "case_name": case_name, - "pod_name": pod_name, "installer": installer, - "scenario": scenario, "criteria": criteria, - "start_date": test_start, "stop_date": test_stop, - "details": details} - try: - with open(file_path, "a+w") as outfile: - json.dump(params, outfile) - outfile.write("\n") - return True - except Exception as e: - logger.error("write result data into a file failed: %s" % e) - return False - - +@decorators.can_dump_request_to_file def push_results_to_db(project, case_name, start_date, stop_date, criteria, details): """ POST results to the Result target DB """ # Retrieve params from CI and conf - url = CONST.results_test_db_url + "/results" + url = get_db_url() + "/results" try: installer = os.environ['INSTALLER_TYPE'] @@ -300,7 +270,7 @@ def get_resolvconf_ns(): while line: ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line) if ip: - resolver.nameservers = [str(ip)] + resolver.nameservers = [ip.group(0)] try: result = resolver.query('opnfv.org')[0] if result != "": @@ -321,6 +291,13 @@ def get_ci_envvars(): return ci_env_var +def execute_command_raise(cmd, info=False, error_msg="", + verbose=True, output_file=None): + ret = execute_command(cmd, info, error_msg, verbose, output_file) + if ret != 0: + raise Exception(error_msg) + + def execute_command(cmd, info=False, error_msg="", verbose=True, output_file=None): if not error_msg: @@ -392,7 +369,7 @@ def get_parameter_from_yaml(parameter, file): value = value.get(element) if value is None: raise ValueError("The parameter %s is not defined in" - " config_functest.yaml" % parameter) + " %s" % (parameter, file)) return value diff --git a/functest/utils/openstack_tacker.py b/functest/utils/openstack_tacker.py index c7ac89af..8327fdbe 100644 --- a/functest/utils/openstack_tacker.py +++ b/functest/utils/openstack_tacker.py @@ -45,8 +45,16 @@ def get_vnfd_id(tacker_client, vnfd_name): return get_id_from_name(tacker_client, 'vnfd', vnfd_name) -def get_vnf_id(tacker_client, vnf_name): - return get_id_from_name(tacker_client, 'vnf', vnf_name) +def get_vnf_id(tacker_client, vnf_name, timeout=5): + vnf_id = None + while vnf_id is None and timeout >= 0: + vnf_id = get_id_from_name(tacker_client, 'vnf', vnf_name) + if vnf_id is None: + logger.info("Could not retrieve ID for vnf with name [%s]." + " Retrying." % vnf_name) + time.sleep(1) + timeout -= 1 + return vnf_id def get_sfc_id(tacker_client, sfc_name): @@ -108,7 +116,8 @@ def list_vnfs(tacker_client, verbose=False): return None -def create_vnf(tacker_client, vnf_name, vnfd_id=None, vnfd_name=None): +def create_vnf(tacker_client, vnf_name, vnfd_id=None, + vnfd_name=None, param_file=None): try: vnf_body = { 'vnf': { @@ -116,6 +125,11 @@ def create_vnf(tacker_client, vnf_name, vnfd_id=None, vnfd_name=None): 'name': vnf_name } } + if param_file is not None: + params = None + with open(param_file) as f: + params = f.read() + vnf_body['vnf']['attributes']['param_values'] = params if vnfd_id is not None: vnf_body['vnf']['vnfd_id'] = vnfd_id else: @@ -130,32 +144,44 @@ def create_vnf(tacker_client, vnf_name, vnfd_id=None, vnfd_name=None): return None -def wait_for_vnf(tacker_client, vnf_id=None, vnf_name=None): +def get_vnf(tacker_client, vnf_id=None, vnf_name=None): try: - _id = None - if vnf_id is not None: - _id = vnf_id - elif vnf_name is not None: - while _id is None: - try: - _id = get_vnf_id(tacker_client, vnf_name) - except: - logger.error("Bazinga") - else: + if vnf_id is None and vnf_name is None: raise Exception('You must specify vnf_id or vnf_name') - while True: - vnf = [v for v in list_vnfs(tacker_client, verbose=True)['vnfs'] - if v['id'] == _id] - vnf = vnf[0] - logger.info('Waiting for vnf {0}'.format(str(vnf))) + + _id = get_vnf_id(tacker_client, vnf_name) if vnf_id is None else vnf_id + + if _id is not None: + all_vnfs = list_vnfs(tacker_client, verbose=True)['vnfs'] + return next((vnf for vnf in all_vnfs if vnf['id'] == _id), None) + else: + raise Exception('Could not retrieve ID from name [%s]' % vnf_name) + + except Exception, e: + logger.error("Could not retrieve VNF [vnf_id=%s, vnf_name=%s] - %s" + % (vnf_id, vnf_name, e)) + return None + + +def wait_for_vnf(tacker_client, vnf_id=None, vnf_name=None, timeout=60): + try: + vnf = get_vnf(tacker_client, vnf_id, vnf_name) + if vnf is None: + raise Exception("Could not retrieve VNF - id='%s', name='%s'" + % vnf_id, vnf_name) + logger.info('Waiting for vnf {0}'.format(str(vnf))) + while vnf['status'] != 'ACTIVE' and timeout >= 0: if vnf['status'] == 'ERROR': - raise Exception('Error when booting vnf %s' % _id) + raise Exception('Error when booting vnf %s' % vnf['id']) elif vnf['status'] == 'PENDING_CREATE': time.sleep(3) - continue - else: - break - return _id + timeout -= 3 + vnf = get_vnf(tacker_client, vnf_id, vnf_name) + + if (timeout < 0): + raise Exception('Timeout when booting vnf %s' % vnf['id']) + + return vnf['id'] except Exception, e: logger.error("error [wait_for_vnf(tacker_client, '%s', '%s')]: %s" % (vnf_id, vnf_name, e)) @@ -189,7 +215,8 @@ def list_sfcs(tacker_client, verbose=False): def create_sfc(tacker_client, sfc_name, chain_vnf_ids=None, - chain_vnf_names=None): + chain_vnf_names=None, + symmetrical=False): try: sfc_body = { 'sfc': { @@ -198,6 +225,8 @@ def create_sfc(tacker_client, sfc_name, 'chain': [] } } + if symmetrical: + sfc_body['sfc']['symmetrical'] = True if chain_vnf_ids is not None: sfc_body['sfc']['chain'] = chain_vnf_ids else: diff --git a/functest/utils/openstack_utils.py b/functest/utils/openstack_utils.py index 64f18504..3093cb55 100755 --- a/functest/utils/openstack_utils.py +++ b/functest/utils/openstack_utils.py @@ -10,7 +10,7 @@ import os import os.path -import subprocess +import re import sys import time @@ -112,12 +112,15 @@ def get_credentials(other_creds={}): def source_credentials(rc_file): - pipe = subprocess.Popen(". %s; env" % rc_file, stdout=subprocess.PIPE, - shell=True) - output = pipe.communicate()[0] - env = dict((line.split("=", 1) for line in output.splitlines())) - os.environ.update(env) - return env + with open(rc_file, "r") as f: + for line in f: + var = line.rstrip('"\n').replace('export ', '').split("=") + # The two next lines should be modified as soon as rc_file + # conforms with common rules. Be aware that it could induce + # issues if value starts with ' + key = re.sub(r'^["\' ]*|[ \'"]*$', '', var[0]) + value = re.sub(r'^["\' ]*|[ \'"]*$', '', "".join(var[1:])) + os.environ[key] = value def get_credentials_for_rally(): diff --git a/requirements.txt b/requirements.txt index b5e78bb5..68b889b3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,4 +28,5 @@ subprocess32 shyaml dnspython Pillow==3.3.0 -click==6.6
\ No newline at end of file +click==6.6 +openbaton-cli==2.2.1-beta7 diff --git a/run_unit_tests.sh b/run_unit_tests.sh index 606aedcd..8e02880a 100755 --- a/run_unit_tests.sh +++ b/run_unit_tests.sh @@ -5,7 +5,7 @@ set -o pipefail # Either Workspace is set (CI) if [ -z $WORKSPACE ] then - WORKSPACE="." + WORKSPACE=`pwd` fi @@ -24,6 +24,13 @@ pip install --upgrade pip pip install -r $WORKSPACE/test-requirements.txt pip install $WORKSPACE +#install releng +cd $WORKSPACE/../ +git clone https://gerrit.opnfv.org/gerrit/releng +pip install releng/modules/ +rm -fr releng +cd $WORKSPACE + export CONFIG_FUNCTEST_YAML=$(pwd)/functest/ci/config_functest.yaml # unit tests # TODO: remove cover-erase @@ -35,6 +42,7 @@ nosetests --with-xunit \ --cover-package=functest.cli \ --cover-package=functest.core.testcase_base \ --cover-package=functest.opnfv_tests.sdn.odl.odl \ + --cover-package=functest.opnfv_tests.vnf.ims \ --cover-package=functest.utils \ --cover-package=functest.opnfv_tests.openstack.rally \ --cover-xml \ diff --git a/test-requirements.txt b/test-requirements.txt index 96b27678..16466b8a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -23,4 +23,5 @@ requests==2.8.0 robotframework==2.9.1 robotframework-requests==0.3.8 robotframework-sshlibrary==2.1.1 +subprocess32==3.2.7 virtualenv==15.1.0 |