diff options
-rw-r--r-- | docker/Dockerfile | 6 | ||||
-rw-r--r-- | docker/Dockerfile.aarch64 | 2 | ||||
-rwxr-xr-x | docker/add_images.sh | 17 | ||||
-rw-r--r-- | docs/devguide/index.rst | 30 | ||||
-rw-r--r-- | docs/internship/testapi_evolution/index.rst | 189 | ||||
-rw-r--r-- | functest/ci/config_aarch64_patch.yaml | 20 | ||||
-rwxr-xr-x | functest/ci/config_functest.yaml | 1 | ||||
-rwxr-xr-x | functest/ci/prepare_env.py | 99 | ||||
-rw-r--r-- | functest/ci/rally_aarch64_patch.conf | 5 | ||||
-rwxr-xr-x | functest/ci/testcases.yaml | 39 | ||||
-rw-r--r-- | functest/opnfv_tests/vnf/ims/cloudify_ims.py | 13 | ||||
-rwxr-xr-x | functest/opnfv_tests/vnf/router/__init__.py | 0 | ||||
-rwxr-xr-x | functest/opnfv_tests/vnf/router/vyos_vrouter.py | 33 | ||||
-rw-r--r-- | functest/tests/unit/opnfv_tests/vnf/__init__.py | 0 | ||||
-rw-r--r-- | functest/tests/unit/opnfv_tests/vnf/ims/__init__.py | 0 | ||||
-rw-r--r-- | functest/tests/unit/opnfv_tests/vnf/ims/test_clearwater.py | 54 | ||||
-rw-r--r-- | functest/tests/unit/opnfv_tests/vnf/ims/test_cloudify_ims.py | 542 | ||||
-rw-r--r-- | functest/tests/unit/opnfv_tests/vnf/ims/test_orchestrator_cloudify.py | 122 | ||||
-rwxr-xr-x | run_unit_tests.sh | 1 | ||||
-rw-r--r-- | test-requirements.txt | 1 |
20 files changed, 1100 insertions, 74 deletions
diff --git a/docker/Dockerfile b/docker/Dockerfile index f59e1f73c..de47e157a 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 @@ -99,6 +100,7 @@ RUN git clone --depth 1 -b $TEMPEST_TAG https://github.com/openstack/tempest.git # 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 @@ -121,9 +123,7 @@ 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 diff --git a/docker/Dockerfile.aarch64 b/docker/Dockerfile.aarch64 index bf8e361f0..a469801f3 100644 --- a/docker/Dockerfile.aarch64 +++ b/docker/Dockerfile.aarch64 @@ -117,6 +117,8 @@ 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 diff --git a/docker/add_images.sh b/docker/add_images.sh new file mode 100755 index 000000000..af2956c29 --- /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/devguide/index.rst b/docs/devguide/index.rst index 42ad04451..eee013678 100644 --- a/docs/devguide/index.rst +++ b/docs/devguide/index.rst @@ -335,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: +--------+--------------------------+-----------------------------------------+ @@ -503,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 =================== @@ -960,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 f2583e2f0..9cca9ebce 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/functest/ci/config_aarch64_patch.yaml b/functest/ci/config_aarch64_patch.yaml new file mode 100644 index 000000000..9a345e3f7 --- /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 7beb9d4bd..489c395ff 100755 --- a/functest/ci/config_functest.yaml +++ b/functest/ci/config_functest.yaml @@ -29,6 +29,7 @@ general: 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 diff --git a/functest/ci/prepare_env.py b/functest/ci/prepare_env.py index 5a9f99cb6..f5bae6a04 100755 --- a/functest/ci/prepare_env.py +++ b/functest/ci/prepare_env.py @@ -12,6 +12,7 @@ import os import re import subprocess import sys +import fileinput import yaml @@ -27,14 +28,18 @@ 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") - -with open(CONFIG_PATCH_PATH) as f: - functest_patch_yaml = yaml.safe_load(f) +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") class PrepareEnvParser(object): @@ -102,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...") @@ -177,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: @@ -210,6 +249,17 @@ def verify_deployment(): 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" @@ -284,34 +334,9 @@ def check_environment(): def print_deployment_info(): - 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): - try: - installer_params = ft_utils.get_parameter_from_yaml( - CONST.INSTALLER_TYPE, installer_params_yaml) - except ValueError as e: - logger.debug('Printing deployment info is not supported for %s' % - CONST.INSTALLER_TYPE) - logger.debug(e) - else: - 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: - logger.info('\n\nDeployment information:\n%s' % - handler.get_deployment_info()) - except Exception as e: - logger.debug("Cannot get deployment information. %s" % e) + if handler: + logger.info('\n\nDeployment information:\n%s' % + handler.get_deployment_info()) def main(**kwargs): @@ -322,9 +347,11 @@ def main(**kwargs): elif kwargs['action'] == "start": logger.info("######### Preparing Functest environment #########\n") check_env_variables() + get_deployment_handler() create_directories() source_rc_file() - patch_config_file() + patch_config_file(CONFIG_PATCH_PATH) + patch_config_file(CONFIG_AARCH64_PATCH_PATH, 'aarch64') verify_deployment() install_rally() install_tempest() diff --git a/functest/ci/rally_aarch64_patch.conf b/functest/ci/rally_aarch64_patch.conf new file mode 100644 index 000000000..a49588bf8 --- /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/testcases.yaml b/functest/ci/testcases.yaml index fd86441b5..ed3a0b843 100755 --- a/functest/ci/testcases.yaml +++ b/functest/ci/testcases.yaml @@ -8,26 +8,14 @@ tiers: operations in the VIM. testcases: - - name: healthcheck - criteria: 'status == "PASS"' - blocking: true - description: >- - This test case verifies the basic OpenStack services like - Keystone, Glance, Cinder, Neutron and Nova. - - dependencies: - installer: '' - scenario: '^((?!lxd).)*$' - - name: snaps_health_check criteria: 'status == "PASS"' - blocking: false + blocking: true description: >- 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).)*$' @@ -325,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 @@ -498,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/opnfv_tests/vnf/ims/cloudify_ims.py b/functest/opnfv_tests/vnf/ims/cloudify_ims.py index 584d780a5..c2c251ad2 100644 --- a/functest/opnfv_tests/vnf/ims/cloudify_ims.py +++ b/functest/opnfv_tests/vnf/ims/cloudify_ims.py @@ -195,8 +195,9 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): 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_exist, flavor_id = os_utils.get_or_create_flavor( @@ -215,7 +216,7 @@ class ImsVnf(vnf_base.VnfOnBoardingBase): 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']) if image_id == '': @@ -256,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'] @@ -340,8 +341,8 @@ 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 main(self, **kwargs): diff --git a/functest/opnfv_tests/vnf/router/__init__.py b/functest/opnfv_tests/vnf/router/__init__.py new file mode 100755 index 000000000..e69de29bb --- /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 000000000..94a3ecfd8 --- /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/opnfv_tests/vnf/__init__.py b/functest/tests/unit/opnfv_tests/vnf/__init__.py new file mode 100644 index 000000000..e69de29bb --- /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 000000000..e69de29bb --- /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 000000000..527f12e59 --- /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 000000000..e25816f01 --- /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 000000000..620b0216f --- /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/run_unit_tests.sh b/run_unit_tests.sh index d60a2d623..8e02880a6 100755 --- a/run_unit_tests.sh +++ b/run_unit_tests.sh @@ -42,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 96b276780..16466b8a3 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 |