├── releasenotes ├── notes │ ├── .placeholder │ ├── advance-image-ubuntu-jammy-2204-11b5ee414d665b66.yaml │ ├── remove-run-tests-opts-152c092bee1dc81d.yaml │ ├── drop-py-2-7-74b8379cab4cdc5a.yaml │ ├── dynamic-routing-base-image-12e76a6d85411a6d.yaml │ ├── agents-client-delete-method-de1a7fb3f845999c.yaml │ ├── deprecate-run-tests-opts-cdf54c17e72d80ac.yaml │ ├── mark-methods-removals-f8b230171c045a3e.yaml │ └── igmp-snooping-8d6d85608df8880a.yaml └── source │ ├── _static │ └── .placeholder │ ├── _templates │ └── .placeholder │ ├── unreleased.rst │ ├── index.rst │ └── README.rst ├── neutron_tempest_plugin ├── __init__.py ├── api │ ├── __init__.py │ ├── admin │ │ ├── __init__.py │ │ ├── test_extension_driver_port_security_admin.py │ │ ├── test_security_groups.py │ │ ├── test_agent_availability_zone.py │ │ ├── test_logging_negative.py │ │ ├── test_floating_ips_admin_actions.py │ │ ├── test_l3_agent_scheduler.py │ │ ├── test_logging.py │ │ ├── test_agent_management.py │ │ └── test_routers_flavors.py │ ├── test_service_type_management.py │ ├── test_availability_zones.py │ ├── test_network_ip_availability_negative.py │ ├── test_metering_negative.py │ ├── base_security_groups.py │ ├── test_extensions.py │ ├── base_routers.py │ ├── test_floating_ips_negative.py │ ├── test_trunk_details.py │ ├── test_ports_negative.py │ ├── test_networks_negative.py │ ├── test_subnetpool_prefix_ops.py │ ├── test_address_scopes_negative.py │ ├── test_subnets.py │ ├── test_ndp_proxy.py │ └── test_port_forwarding_negative.py ├── sfc │ ├── __init__.py │ ├── services │ │ ├── __init__.py │ │ └── flowclassifier_client.py │ └── tests │ │ ├── __init__.py │ │ ├── api │ │ ├── __init__.py │ │ └── test_flowclassifier_extensions.py │ │ ├── scenario │ │ ├── __init__.py │ │ ├── base.py │ │ └── manager.py │ │ ├── flowclassifier_client.py │ │ └── sfc_client.py ├── bgpvpn │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── scenario │ │ └── __init__.py │ ├── services │ │ ├── __init__.py │ │ └── bgpvpn_client.py │ └── base.py ├── common │ ├── __init__.py │ └── tempest_fixtures.py ├── fwaas │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── fwaas_v2_base.py │ ├── common │ │ └── __init__.py │ ├── scenario │ │ ├── __init__.py │ │ ├── fwaas_v2_manager.py │ │ └── fwaas_v2_base.py │ └── services │ │ ├── __init__.py │ │ └── v2_client.py ├── scenario │ ├── __init__.py │ ├── admin │ │ ├── __init__.py │ │ └── test_floatingip.py │ ├── constants.py │ ├── exceptions.py │ ├── test_basic.py │ ├── test_dvr.py │ ├── test_portsecurity.py │ ├── test_fip64.py │ ├── test_local_ip.py │ └── test_ports.py ├── services │ ├── __init__.py │ ├── bgp │ │ └── __init__.py │ └── network │ │ ├── __init__.py │ │ └── json │ │ └── __init__.py ├── vpnaas │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── scenario │ │ ├── __init__.py │ │ └── base_vpnaas.py │ └── services │ │ ├── __init__.py │ │ └── clients_vpnaas.py ├── tap_as_a_service │ ├── __init__.py │ ├── api │ │ └── __init__.py │ ├── scenario │ │ └── __init__.py │ ├── services │ │ ├── __init__.py │ │ └── taas_client.py │ └── base.py ├── neutron_dynamic_routing │ ├── __init__.py │ ├── api │ │ └── __init__.py │ └── scenario │ │ ├── __init__.py │ │ ├── basic │ │ ├── __init__.py │ │ └── base.py │ │ ├── ipv4 │ │ └── __init__.py │ │ ├── ipv6 │ │ └── __init__.py │ │ └── README ├── README.rst ├── plugin.py └── exceptions.py ├── doc ├── source │ ├── readme.rst │ ├── contributing.rst │ ├── test_descriptions.rst │ ├── installation.rst │ ├── index.rst │ └── conf.py └── requirements.txt ├── roles ├── docker-setup │ ├── files │ │ ├── 52_docker_for_tempest │ │ └── docker_apparmor │ └── tasks │ │ └── main.yml └── multi-node-setup │ ├── defaults │ └── main.yaml │ ├── tasks │ └── main.yaml │ └── README.rst ├── playbooks ├── dynamic-routing-pre-run.yaml ├── plugin-ovn-scenario-pre-run.yaml ├── dvr-multinode-scenario-pre-run.yaml └── linuxbridge-scenario-pre-run.yaml ├── .stestr.conf ├── .mailmap ├── .gitreview ├── HACKING.rst ├── test-requirements.txt ├── devstack ├── settings ├── README.rst ├── plugin.sh └── functions.sh ├── requirements.txt ├── README.rst ├── CONTRIBUTING.rst ├── setup.py ├── .gitignore ├── setup.cfg ├── .pre-commit-config.yaml ├── tools └── misc-sanity-checks.sh ├── tox.ini └── zuul.d └── base-nested-switch.yaml /releasenotes/notes/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/bgpvpn/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/vpnaas/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /releasenotes/source/_static/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /releasenotes/source/_templates/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/bgpvpn/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/services/bgp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/vpnaas/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/bgpvpn/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/bgpvpn/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/admin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/services/network/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/tap_as_a_service/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/vpnaas/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/vpnaas/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/source/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../README.rst 2 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/services/network/json/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/tap_as_a_service/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/tap_as_a_service/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/tap_as_a_service/services/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/scenario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv4/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/scenario/ipv6/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /roles/docker-setup/files/52_docker_for_tempest: -------------------------------------------------------------------------------- 1 | tempest ALL=(ALL) NOPASSWD: ALL 2 | -------------------------------------------------------------------------------- /playbooks/dynamic-routing-pre-run.yaml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | roles: 3 | - docker-setup 4 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=${OS_TEST_PATH:-./neutron_tempest_plugin} 3 | top_dir=./ 4 | -------------------------------------------------------------------------------- /playbooks/plugin-ovn-scenario-pre-run.yaml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | roles: 3 | - docker-setup 4 | -------------------------------------------------------------------------------- /playbooks/dvr-multinode-scenario-pre-run.yaml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | roles: 3 | - multi-node-setup 4 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # Format is: 2 | # 3 | # 4 | -------------------------------------------------------------------------------- /roles/multi-node-setup/defaults/main.yaml: -------------------------------------------------------------------------------- 1 | infra_bridge_name: br-infra 2 | neutron_external_bridge_name: br-ex 3 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/neutron-tempest-plugin.git 5 | -------------------------------------------------------------------------------- /doc/source/contributing.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | .. include:: ../../CONTRIBUTING.rst 5 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | reno>=3.1.0 # Apache-2.0 2 | sphinx>=2.2.0 # BSD 3 | openstackdocstheme>=2.2.1 # Apache-2.0 4 | 5 | -------------------------------------------------------------------------------- /doc/source/test_descriptions.rst: -------------------------------------------------------------------------------- 1 | Description of Tests 2 | ==================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | tests/modules 8 | -------------------------------------------------------------------------------- /HACKING.rst: -------------------------------------------------------------------------------- 1 | openstack Style Commandments 2 | ============================ 3 | 4 | Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ 5 | -------------------------------------------------------------------------------- /releasenotes/source/unreleased.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Current Series Release Notes 3 | ============================== 4 | 5 | .. release-notes:: 6 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | hacking>=6.1.0,<6.2.0 # Apache-2.0 2 | 3 | flake8-import-order>=0.18.0,<0.19.0 # LGPLv3 4 | oslotest>=3.2.0 # Apache-2.0 5 | stestr>=1.0.0 # Apache-2.0 6 | testtools>=2.2.0 # MIT 7 | -------------------------------------------------------------------------------- /releasenotes/notes/advance-image-ubuntu-jammy-2204-11b5ee414d665b66.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | other: 3 | - | 4 | The testing advanced image used in some jobs has been bumped from Ubuntu 5 | Focal 20.04 to Ubuntu Jammy 22.04. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-run-tests-opts-152c092bee1dc81d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The following options have been removed. 5 | 6 | - ``[bgpvpn] run_bgpvpn_tests`` 7 | - ``[fwaas] run_fwaas_tests`` 8 | - ``[sfc] run_sfc_tests`` 9 | -------------------------------------------------------------------------------- /doc/source/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | For installation and usage of neutron-tempest-plugin, please refer 6 | to the documentation in the Neutron tree: 7 | 8 | https://docs.openstack.org/neutron/latest/contributor/testing/tempest.html 9 | -------------------------------------------------------------------------------- /releasenotes/source/index.rst: -------------------------------------------------------------------------------- 1 | ==================================== 2 | Neutron Tempest Plugin Release Notes 3 | ==================================== 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | unreleased 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | README.rst 14 | -------------------------------------------------------------------------------- /releasenotes/notes/drop-py-2-7-74b8379cab4cdc5a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 2.7 support has been dropped. Last release of neutron-tempest-plugin 5 | to support py2.7 is OpenStack Train. The minimum version of Python now 6 | supported by neutron-tempest-plugin is Python 3.6. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/dynamic-routing-base-image-12e76a6d85411a6d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The new ``[dynamic_routing] base_image`` option has been added. This option 5 | allows customizing the reference of the base container image used for 6 | connectivity check in dynamic routing plugin tests. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/agents-client-delete-method-de1a7fb3f845999c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added a new method ``delete_agent`` to the AgentsClient class that 5 | implements agent deletion according to the neutron API. 6 | https://developer.openstack.org/api-ref/network/v2/index.html#delete-agent 7 | 8 | -------------------------------------------------------------------------------- /playbooks/linuxbridge-scenario-pre-run.yaml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | tasks: 3 | # TODO(slaweq): remove it when nftables will support syntax for src and 4 | # destination IP addresses in arp tables: 5 | - include_role: 6 | name: legacy_ebtables 7 | when: ansible_distribution_release | lower in ['focal', 'jammy'] 8 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-run-tests-opts-cdf54c17e72d80ac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The following options have been deprecated, in favor of the mechanism to 5 | enable/disable tests according to the available extensions. 6 | 7 | - ``[bgpvpn] run_bgpvpn_tests`` 8 | - ``[fwaas] run_fwaas_tests`` 9 | - ``[sfc] run_sfc_tests`` 10 | -------------------------------------------------------------------------------- /releasenotes/notes/mark-methods-removals-f8b230171c045a3e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | features: 4 | - | 5 | Add new 'debtcollector' dependency with the purpose of deprecating methods 6 | that are going to be removed. 7 | 8 | deprecations: 9 | - | 10 | Deprecate method BaseTempestTestCase.create_and_associate_floatingip after 11 | replacing it with method BaseNetworkTest.create_floatingip. 12 | -------------------------------------------------------------------------------- /devstack/settings: -------------------------------------------------------------------------------- 1 | GITREPO["neutron-tempest-plugin"]=${NEUTRON_TEMPEST_REPO:-${GIT_BASE}/openstack/neutron-tempest-plugin.git} 2 | GITDIR["neutron-tempest-plugin"]=$DEST/neutron-tempest-plugin 3 | GITBRANCH["neutron-tempest-plugin"]=master 4 | 5 | ADVANCED_IMAGE_NAME=${ADVANCED_IMAGE_NAME:-""} 6 | ADVANCED_INSTANCE_TYPE=${ADVANCED_INSTANCE_TYPE:-$DEFAULT_INSTANCE_TYPE} 7 | ADVANCED_INSTANCE_USER=${ADVANCED_INSTANCE_USER:-$DEFAULT_INSTANCE_USER} 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pbr>=3.0.0 # Apache-2.0 2 | neutron-lib>=3.21.1 # Apache-2.0 3 | oslo.config>=5.2.0 # Apache-2.0 4 | netaddr>=0.7.18 # BSD 5 | os-ken>=0.3.0 # Apache-2.0 6 | oslo.log>=3.36.0 # Apache-2.0 7 | oslo.serialization>=2.20.0 # Apache-2.0 8 | oslo.utils>=3.33.0 # Apache-2.0 9 | packaging>=20.4 # Apache-2.0 10 | paramiko>=2.0.0 # LGPLv2.1+ 11 | tempest>=29.2.0 # Apache-2.0 12 | tenacity>=3.2.1 # Apache-2.0 13 | ddt>=1.0.1 # MIT 14 | testtools>=2.2.0 # MIT 15 | debtcollector>=1.2.0 # Apache-2.0 16 | -------------------------------------------------------------------------------- /releasenotes/notes/igmp-snooping-8d6d85608df8880a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Enhanced the ``test_multicast_between_vms_on_same_network`` adding 5 | IGMP test coverage to it. A new VM running tcpdump is spawned as 6 | part of the test to verify whether the traffic is reaching it or not. 7 | upgrade: 8 | - | 9 | Add a new configuration option called ``is_igmp_snooping_enabled`` 10 | to enable/disable IGMP testing as part of the 11 | ``test_multicast_between_vms_on_same_network`` test case. 12 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/README.rst: -------------------------------------------------------------------------------- 1 | WARNING 2 | ======= 3 | 4 | Some files under this path were copied from tempest as part of the move of the 5 | api tests, and they will be removed as required over time to minimize the 6 | dependency on the tempest testing framework. While it exists, only 7 | neutron_tempest_plugin.* should be importing files from this path. 8 | neutron_tempest_plugin.config uses the global cfg.CONF instance and importing it 9 | outside of the api tests has the potential to break Neutron's use of cfg.CONF. 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Neutron Tempest Plugin 3 | ====================== 4 | 5 | Tempest plugin for Neutron project. 6 | 7 | It contains the tempest plugin for the functional testing of Neutron Project. 8 | 9 | * Free software: Apache license 10 | * Documentation: https://docs.openstack.org/neutron/latest/ 11 | * Source: https://opendev.org/openstack/neutron-tempest-plugin 12 | * Bugs: https://bugs.launchpad.net/neutron - tag: tempest 13 | * Release notes: https://docs.openstack.org/releasenotes/neutron-tempest-plugin/ 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | The source repository for this project can be found at: 2 | 3 | https://opendev.org/openstack/neutron-tempest-plugin 4 | 5 | Pull requests submitted through GitHub are not monitored. 6 | 7 | To start contributing to OpenStack, follow the steps in the contribution guide 8 | to set up and use Gerrit: 9 | 10 | https://docs.openstack.org/contributors/code-and-documentation/quick-start.html 11 | 12 | Bugs should be filed on Launchpad: 13 | 14 | https://bugs.launchpad.net/neutron 15 | 16 | And the tag 'tempest' should be added to the bug. 17 | -------------------------------------------------------------------------------- /devstack/README.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Enabling in Devstack 3 | ==================== 4 | 5 | **WARNING**: the stack.sh script must be run in a disposable VM that is not 6 | being created automatically, see the README.md file in the "devstack" 7 | repository. See contrib/vagrant to create a vagrant VM. 8 | 9 | 1. Download DevStack:: 10 | 11 | git clone https://opendev.org/openstack/devstack.git 12 | cd devstack 13 | 14 | 2. Add this repo as an external repository:: 15 | 16 | > cat local.conf 17 | [[local|localrc]] 18 | enable_plugin neutron-tempest-plugin https://opendev.org/openstack/neutron-tempest-plugin 19 | 20 | 3. run ``stack.sh`` 21 | 22 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. openstack documentation master file, created by 2 | sphinx-quickstart on Tue Jul 9 22:26:36 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ====================================================== 7 | Welcome to the documentation of neutron_tempest_plugin 8 | ====================================================== 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | readme 16 | installation 17 | contributing 18 | test_descriptions 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import setuptools 17 | 18 | setuptools.setup( 19 | setup_requires=['pbr>=2.0.0'], 20 | pbr=True) 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg* 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | cover/ 26 | .coverage* 27 | !.coveragerc 28 | .tox 29 | .venv 30 | .stestr/ 31 | 32 | # Translations 33 | *.mo 34 | 35 | # Mr Developer 36 | .mr.developer.cfg 37 | .project 38 | .pydevproject 39 | 40 | # Complexity 41 | output/*.html 42 | output/*/index.html 43 | 44 | # Sphinx 45 | doc/build 46 | doc/source/tests 47 | 48 | # pbr generates these 49 | AUTHORS 50 | ChangeLog 51 | 52 | # Editors 53 | *~ 54 | .*.swp 55 | .*sw? 56 | 57 | # Files created by releasenotes build 58 | releasenotes/build 59 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | SERVER_STATUS_ACTIVE = 'ACTIVE' 16 | DEFAULT_SECURITY_GROUP = 'default' 17 | LIMIT_KILO_BITS_PER_SECOND = 1000 18 | LIMIT_KILO_BYTES = 1000 19 | SOCKET_CONNECT_TIMEOUT = 60 20 | -------------------------------------------------------------------------------- /roles/multi-node-setup/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | - name: Ensure the infra bridge exists 2 | become: yes 3 | command: >- 4 | ovs-vsctl --may-exist add-br {{ infra_bridge_name }} 5 | 6 | - name: Ensure the Neutron external bridge exists 7 | become: yes 8 | command: >- 9 | ovs-vsctl --may-exist add-br {{ neutron_external_bridge_name }} 10 | 11 | - name: Create patch port between bridges 12 | become: yes 13 | command: >- 14 | ovs-vsctl --may-exist add-port {{ infra_bridge_name }} patch-{{ neutron_external_bridge_name }} 15 | -- set interface patch-{{ neutron_external_bridge_name }} type=patch options:peer=patch-{{ infra_bridge_name }} 16 | -- --may-exist add-port {{ neutron_external_bridge_name }} patch-{{ infra_bridge_name }} 17 | -- set interface patch-{{ infra_bridge_name }} type=patch options:peer=patch-{{ neutron_external_bridge_name }} 18 | -------------------------------------------------------------------------------- /roles/multi-node-setup/README.rst: -------------------------------------------------------------------------------- 1 | Set up connection between infra bridge and Neutron external bridge 2 | 3 | Network topology used in CI multinode jobs is described In `Devstack documention 4 | `_ 5 | 6 | In case when DVR is used, there is also additional bridge ``br-infra`` added 7 | on each node to provide connectivity to floating IPs from main node. 8 | 9 | This bridge needs to be connected with bridge used by Neutron as 10 | external bridge. Typically it is ``br-ex`` and this role adds patch ports 11 | between those bridges. 12 | 13 | **Role Variables** 14 | 15 | .. zuul:rolevar:: neutron_external_bridge_name 16 | :default: br-ex 17 | 18 | Name of the Neutron external bridge. 19 | 20 | .. zuul:rolevar:: infra_bridge_name 21 | :default: br-infra 22 | 23 | Name of the infra bridge. 24 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/common/tempest_fixtures.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 IBM Corp. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from oslo_concurrency.fixture import lockutils 17 | 18 | 19 | class LockFixture(lockutils.LockFixture): 20 | def __init__(self, name): 21 | super(LockFixture, self).__init__(name, 'tempest-') 22 | -------------------------------------------------------------------------------- /devstack/plugin.sh: -------------------------------------------------------------------------------- 1 | # Directory where this plugin.sh file is 2 | NEUTRON_TEMPEST_PLUGIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 3 | 4 | source "${NEUTRON_TEMPEST_PLUGIN_DIR}/customize_image.sh" 5 | 6 | # install_neutron_tempest_plugin 7 | function install_neutron_tempest_plugin { 8 | setup_dev_lib "neutron-tempest-plugin" 9 | } 10 | 11 | if [[ "$1" == "stack" ]]; then 12 | case "$2" in 13 | install) 14 | if [[ "$INSTALL_TEMPEST" == "True" ]]; then 15 | echo_summary "Installing neutron-tempest-plugin" 16 | install_neutron_tempest_plugin 17 | fi 18 | ;; 19 | test-config) 20 | echo_summary "Configuring neutron-tempest-plugin tempest options" 21 | configure_advanced_image 22 | create_flavor_for_advance_image $ADVANCED_INSTANCE_TYPE 256 4 1 23 | configure_flavor_for_advanced_image 24 | esac 25 | fi 26 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/vpnaas/scenario/base_vpnaas.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Midokura SARL 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from neutron_tempest_plugin.scenario import base 17 | from neutron_tempest_plugin.vpnaas.api import base_vpnaas as base_api 18 | 19 | 20 | class BaseTempestTestCase(base_api.BaseNetworkTest, base.BaseTempestTestCase): 21 | pass 22 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/api/fwaas_v2_base.py: -------------------------------------------------------------------------------- 1 | # All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.api.network import base 16 | from tempest import config 17 | 18 | from neutron_tempest_plugin.fwaas.common import fwaas_v2_client 19 | 20 | CONF = config.CONF 21 | 22 | 23 | class BaseFWaaSTest(fwaas_v2_client.FWaaSClientMixin, base.BaseNetworkTest): 24 | pass 25 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = neutron-tempest-plugin 3 | summary = Tempest plugin for Neutron Project 4 | description_file = 5 | README.rst 6 | author = OpenStack 7 | author_email = openstack-discuss@lists.openstack.org 8 | home_page = https://opendev.org/openstack/neutron-tempest-plugin 9 | python_requires = >=3.9 10 | classifier = 11 | Environment :: OpenStack 12 | Intended Audience :: Information Technology 13 | Intended Audience :: System Administrators 14 | License :: OSI Approved :: Apache Software License 15 | Operating System :: POSIX :: Linux 16 | Programming Language :: Python 17 | Programming Language :: Python :: 3 18 | Programming Language :: Python :: 3.9 19 | Programming Language :: Python :: 3.10 20 | Programming Language :: Python :: 3.11 21 | Programming Language :: Python :: 3.12 22 | 23 | [files] 24 | packages = 25 | neutron_tempest_plugin 26 | 27 | [entry_points] 28 | tempest.test_plugins = 29 | neutron_tests = neutron_tempest_plugin.plugin:NeutronTempestPlugin 30 | -------------------------------------------------------------------------------- /roles/docker-setup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install and configure docker 2 | become: yes 3 | package: 4 | name: docker.io 5 | state: present 6 | 7 | - name: Install docker-buildx (only for Ubuntu and Debian) 8 | become: yes 9 | package: 10 | name: docker-buildx 11 | state: present 12 | when: 13 | - (ansible_facts['distribution'] | lower) in ['ubuntu', 'debian'] 14 | 15 | - name: Copy 52_docker_for_tempest to /etc/sudoers.d 16 | copy: 17 | src: 52_docker_for_tempest 18 | dest: /etc/sudoers.d 19 | owner: root 20 | group: root 21 | mode: 0440 22 | become: yes 23 | 24 | - name: Copy docker_apparmor to /etc/apparmor.d 25 | copy: 26 | src: docker_apparmor 27 | dest: /etc/apparmor.d 28 | owner: root 29 | group: root 30 | mode: 0640 31 | become: yes 32 | 33 | - name: Ensure apparmor is restarted 34 | become: yes 35 | service: 36 | name: apparmor 37 | state: restarted 38 | ignore_errors: yes 39 | 40 | - name: Ensure docker engine is restarted 41 | become: yes 42 | service: 43 | name: docker 44 | state: restarted 45 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_service_type_management.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from tempest.lib import decorators 14 | 15 | from neutron_tempest_plugin.api import base 16 | 17 | 18 | class ServiceTypeManagementTest(base.BaseNetworkTest): 19 | 20 | required_extensions = ['service-type'] 21 | 22 | @decorators.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6') 23 | def test_service_provider_list(self): 24 | body = self.client.list_service_providers() 25 | self.assertIsInstance(body['service_providers'], list) 26 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_language_version: 3 | # force all unspecified python hooks to run python3 4 | python: python3 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.5.0 8 | hooks: 9 | - id: trailing-whitespace 10 | - id: mixed-line-ending 11 | args: ['--fix', 'lf'] 12 | exclude: '.*\.(svg)$' 13 | - id: check-byte-order-marker 14 | - id: check-executables-have-shebangs 15 | - id: check-merge-conflict 16 | - id: debug-statements 17 | - id: check-yaml 18 | files: .*\.(yaml|yml)$ 19 | - repo: https://github.com/lucas-c/pre-commit-hooks 20 | rev: v1.5.4 21 | hooks: 22 | - id: remove-tabs 23 | exclude: '.*\.(svg)$' 24 | - repo: local 25 | hooks: 26 | - id: flake8 27 | name: flake8 28 | additional_dependencies: 29 | - hacking>=3.2.0,<3.3.0 30 | language: python 31 | entry: flake8 32 | files: '^.*\.py$' 33 | exclude: '^(doc|releasenotes|tools)/.*$' 34 | - repo: https://opendev.org/openstack/hacking 35 | rev: 7.0.0 36 | hooks: 37 | - id: hacking 38 | additional_dependencies: ['neutron-lib'] 39 | exclude: '^(doc|releasenotes|tools)/.*$' 40 | -------------------------------------------------------------------------------- /roles/docker-setup/files/docker_apparmor: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | profile docker-default flags=(attach_disconnected,mediate_deleted) { 4 | 5 | #include 6 | 7 | network, 8 | capability, 9 | file, 10 | umount, 11 | 12 | deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) 13 | # deny write to files not in /proc//** or /proc/sys/** 14 | deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, 15 | deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) 16 | deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ 17 | deny @{PROC}/sysrq-trigger rwklx, 18 | deny @{PROC}/mem rwklx, 19 | deny @{PROC}/kmem rwklx, 20 | deny @{PROC}/kcore rwklx, 21 | 22 | deny mount, 23 | 24 | deny /sys/[^f]*/** wklx, 25 | deny /sys/f[^s]*/** wklx, 26 | deny /sys/fs/[^c]*/** wklx, 27 | deny /sys/fs/c[^g]*/** wklx, 28 | deny /sys/fs/cg[^r]*/** wklx, 29 | deny /sys/firmware/efi/efivars/** rwklx, 30 | deny /sys/kernel/security/** rwklx, 31 | 32 | # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container 33 | ptrace (trace,read) peer=docker-default, 34 | } 35 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_availability_zones.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 AT&T Corporation. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.common import utils 17 | from tempest.lib import decorators 18 | 19 | from neutron_tempest_plugin.api import base 20 | 21 | 22 | class ListAvailableZonesTest(base.BaseNetworkTest): 23 | 24 | @decorators.idempotent_id('5a8a8a1a-c265-11e8-a611-080027758b73') 25 | @utils.requires_ext(extension="availability_zone", 26 | service="network") 27 | def test_list_available_zones(self): 28 | body = self.client.list_availability_zones() 29 | self.assertIsNotNone(body) 30 | self.assertIsInstance(body['availability_zones'], list) 31 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/scenario/README: -------------------------------------------------------------------------------- 1 | scenario tests use the following environment. 2 | 3 | diagram: 4 | 5 | ----------------------+--------------- tenant 6 | | network 7 | +--------+ 8 | | router | 9 | +--------+ 10 | | 11 | -----+----------------+--------------- provider 12 | | | network 13 | +---------+ | 14 | | dragent | | 15 | +---------+ | 16 | | | 17 | | +--------------+ 18 | | | 19 | +--------+ 20 | | docker | 21 | | bridge | 22 | +--------+ 23 | | 24 | +-----------+------------+------- 25 | | | 26 | +---------+ +---------+ 27 | docker | quagga1 | | quagga2 | ... 28 | container +---------+ +---------+ 29 | 30 | 31 | docker container environment is provided by test tool of os-ken. 32 | It has the following functions: 33 | - build and remove a container image. 34 | - run, stop and remove a container. 35 | - some operations to quagga container. 36 | - get some information from quagga container. 37 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from tempest.lib import exceptions 16 | 17 | TempestException = exceptions.TempestException 18 | 19 | 20 | class QoSLimitReached(TempestException): 21 | message = "Limit reached, limit = %(limit)d" 22 | 23 | 24 | class SocketConnectionRefused(TempestException): 25 | message = "Unable to connect to %(host)s port %(port)d:Connection Refused" 26 | 27 | 28 | class ConnectionTimeoutException(TempestException): 29 | message = "Timeout connecting to %(host)s port %(port)d" 30 | 31 | 32 | class FileCreationFailedException(TempestException): 33 | message = "File %(file)s has not been created or has the wrong size" 34 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_network_ip_availability_negative.py: -------------------------------------------------------------------------------- 1 | # All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from oslo_utils import uuidutils 16 | from tempest.lib import decorators 17 | from tempest.lib import exceptions as lib_exc 18 | 19 | from neutron_tempest_plugin.api import test_network_ip_availability as net_ip 20 | 21 | 22 | class NetworksIpAvailabilityNegativeTest(net_ip.NetworksIpAvailabilityTest): 23 | 24 | @decorators.attr(type='negative') 25 | @decorators.idempotent_id('3b8693eb-6c57-4ea1-ab84-3730c9ee9c84') 26 | def test_network_availability_nonexistent_network_id(self): 27 | self.assertRaises(lib_exc.NotFound, 28 | self.admin_client.show_network_ip_availability, 29 | uuidutils.generate_uuid()) 30 | -------------------------------------------------------------------------------- /tools/misc-sanity-checks.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | TMPDIR=`mktemp -d /tmp/${0##*/}.XXXXXX` || exit 1 16 | export TMPDIR 17 | trap "rm -rf $TMPDIR" EXIT 18 | 19 | FAILURES=$TMPDIR/failures 20 | 21 | check_no_duplicate_api_test_idempotent_ids() { 22 | # For API tests, an idempotent ID is assigned to each single API test, 23 | # those IDs should be unique 24 | output=$(check-uuid --package neutron_tempest_plugin) 25 | if [ "$?" -ne 0 ]; then 26 | echo "There are duplicate idempotent ids in the API tests" >>$FAILURES 27 | echo "please, assign unique uuids to each API test:" >>$FAILURES 28 | echo "$output" >>$FAILURES 29 | fi 30 | } 31 | 32 | check_no_duplicate_api_test_idempotent_ids 33 | 34 | # Fail, if there are emitted failures 35 | if [ -f $FAILURES ]; then 36 | cat $FAILURES 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_extension_driver_port_security_admin.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Cisco Systems, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib import decorators 17 | from tempest.lib import exceptions as lib_exc 18 | 19 | from neutron_tempest_plugin.api import base 20 | 21 | 22 | class PortSecurityAdminTests(base.BaseAdminNetworkTest): 23 | 24 | required_extensions = ['port-security'] 25 | 26 | @decorators.attr(type='negative') 27 | @decorators.idempotent_id('d39a96e2-2dea-4feb-8093-e7ac991ce6f8') 28 | def test_create_port_security_false_on_shared_network(self): 29 | network = self.create_network(shared=True) 30 | self.assertTrue(network['shared']) 31 | self.create_subnet(network, client=self.admin_client) 32 | self.assertRaises(lib_exc.Forbidden, self.create_port, 33 | network, port_security_enabled=False) 34 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_metering_negative.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 FUJITSU LIMITED 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from neutron_lib.api.definitions import metering as metering_apidef 16 | from neutron_lib.db import constants as db_const 17 | from tempest.lib import decorators 18 | from tempest.lib import exceptions as lib_exc 19 | 20 | from neutron_tempest_plugin.api import base 21 | 22 | LONG_NAME_NG = 'x' * (db_const.NAME_FIELD_SIZE + 1) 23 | 24 | 25 | class MeteringNegativeTestJSON(base.BaseAdminNetworkTest): 26 | 27 | required_extensions = [metering_apidef.ALIAS] 28 | 29 | @decorators.attr(type='negative') 30 | @decorators.idempotent_id('8b3f7c84-9d37-4771-8681-bfd2c07f3c2d') 31 | def test_create_metering_label_with_too_long_name(self): 32 | self.assertRaises(lib_exc.BadRequest, 33 | self.admin_client.create_metering_label, 34 | name=LONG_NAME_NG) 35 | -------------------------------------------------------------------------------- /releasenotes/source/README.rst: -------------------------------------------------------------------------------- 1 | ========================================== 2 | Neutron Tempest Plugin Release Notes Howto 3 | ========================================== 4 | 5 | Release notes are a new feature for documenting new features in 6 | OpenStack projects. Background on the process, tooling, and 7 | methodology is documented in a `mailing list post by Doug Hellmann `_. 8 | 9 | Writing release notes 10 | --------------------- 11 | 12 | For information on how to create release notes, please consult the 13 | `reno documentation `__. 14 | 15 | Please keep the following in your mind when you write release notes. 16 | 17 | * **Avoid using "prelude" section** for individual release notes. 18 | "prelude" section is for general comments about the release. 19 | * **Use one entry per section** (like "feature" or "upgrade"). 20 | All entries which belong to a same release will be merged and rendered, 21 | so there is less meaning to use multiple entries by a single topic. 22 | 23 | Maintaining release notes 24 | ------------------------- 25 | 26 | .. warning:: 27 | 28 | Avoid modifying an existing release note file even though it is related 29 | to your change. If you modify a release note file of a past release, 30 | the whole content will be shown in a latest release. The only allowed 31 | case is to update a release note in a same release. 32 | 33 | If you need to update a release note of a past release, 34 | edit a corresponding release note file in a stable branch directly. 35 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/flowclassifier_client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Futurewei. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest import config 16 | 17 | from neutron_tempest_plugin.sfc.services import flowclassifier_client 18 | 19 | CONF = config.CONF 20 | 21 | 22 | class FlowClassifierClientMixin(object): 23 | 24 | @classmethod 25 | def resource_setup(cls): 26 | super(FlowClassifierClientMixin, cls).resource_setup() 27 | manager = cls.os_admin 28 | cls.flowclassifier_client = ( 29 | flowclassifier_client.FlowClassifierClient( 30 | manager.auth_provider, 31 | CONF.network.catalog_type, 32 | CONF.network.region or CONF.identity.region, 33 | endpoint_type=CONF.network.endpoint_type, 34 | build_interval=CONF.network.build_interval, 35 | build_timeout=CONF.network.build_timeout, 36 | **manager.default_params 37 | ) 38 | ) 39 | 40 | @classmethod 41 | def create_flowclassifier(cls, **kwargs): 42 | body = cls.flowclassifier_client.create_flowclassifier( 43 | **kwargs) 44 | fc = body['flow_classifier'] 45 | return fc 46 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_security_groups.py: -------------------------------------------------------------------------------- 1 | # All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib import decorators 16 | 17 | from neutron_tempest_plugin.api import base 18 | 19 | 20 | class SecGroupAdminTest(base.BaseNetworkTest): 21 | required_extensions = ['security-group'] 22 | credentials = ['primary', 'admin'] 23 | 24 | @classmethod 25 | def setup_clients(cls): 26 | super(SecGroupAdminTest, cls).setup_clients() 27 | cls.admin_client = cls.os_admin.network_client 28 | cls.identity_admin_client = cls.os_admin.projects_client 29 | 30 | @decorators.idempotent_id('44f1e1c4-af10-4aa0-972f-87c1c8fa25cc') 31 | def test_security_group_recreated_on_port_update(self): 32 | network = self.create_network() 33 | self.create_subnet(network) 34 | port = self.create_port(network, security_groups=[]) 35 | for sg in self.client.list_security_groups()['security_groups']: 36 | if sg['name'] == 'default': 37 | self.admin_client.delete_security_group(sg['id']) 38 | self.update_port(port, name='update') 39 | names = [ 40 | sg['name'] 41 | for sg in self.client.list_security_groups()['security_groups'] 42 | ] 43 | self.assertIn('default', names) 44 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/base_security_groups.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from neutron_lib import constants 17 | 18 | 19 | # NOTE(yamamoto): The list of protocols here is what we had in Ocata. 20 | # (neutron-lib 1.1.0) 21 | # Why don't we just use neutron_lib.constants.IP_PROTOCOL_MAP etc here? 22 | # Tempest is branchless and thus supposed to work against older deployments. 23 | # Also, it's supposed to work against other implementations, which might not 24 | # support the same set as the reference implementation. Ideally SG can have 25 | # a way to discover the set of usable protocols. But for now, we need to be 26 | # conservative. 27 | 28 | V4_PROTOCOL_NAMES = { 29 | 'ah', 30 | 'dccp', 31 | 'egp', 32 | 'esp', 33 | 'gre', 34 | 'icmp', 35 | 'igmp', 36 | 'ospf', 37 | 'pgm', 38 | 'rsvp', 39 | 'sctp', 40 | 'tcp', 41 | 'udp', 42 | 'udplite', 43 | 'vrrp', 44 | } 45 | 46 | V4_PROTOCOL_INTS = {v 47 | for k, v in constants.IP_PROTOCOL_MAP.items() 48 | if k in V4_PROTOCOL_NAMES} 49 | 50 | V6_PROTOCOL_NAMES = { 51 | 'ipv6-encap', 52 | 'ipv6-frag', 53 | 'ipv6-icmp', 54 | 'ipv6-nonxt', 55 | 'ipv6-opts', 56 | 'ipv6-route', 57 | } 58 | 59 | V6_PROTOCOL_INTS = {v 60 | for k, v in constants.IP_PROTOCOL_MAP.items() 61 | if k in V6_PROTOCOL_NAMES} 62 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/services/flowclassifier_client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Futurewei. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib import exceptions as lib_exc 16 | from tempest.lib.services.network import base 17 | 18 | 19 | class FlowClassifierClient(base.BaseNetworkClient): 20 | 21 | def create_flowclassifier(self, **kwargs): 22 | uri = '/sfc/flow_classifiers' 23 | post_data = {'flow_classifier': kwargs} 24 | return self.create_resource(uri, post_data) 25 | 26 | def update_flowclassifier(self, flowclassifier_id, **kwargs): 27 | uri = '/sfc/flow_classifiers/%s' % flowclassifier_id 28 | post_data = {'flow_classifier': kwargs} 29 | return self.update_resource(uri, post_data) 30 | 31 | def show_flowclassifier(self, flowclassifier_id, **fields): 32 | uri = '/sfc/flow_classifiers/%s' % flowclassifier_id 33 | return self.show_resource(uri, **fields) 34 | 35 | def delete_flowclassifier(self, flowclassifier_id): 36 | uri = '/sfc/flow_classifiers/%s' % flowclassifier_id 37 | return self.delete_resource(uri) 38 | 39 | def list_flowclassifiers(self, **filters): 40 | uri = '/sfc/flow_classifiers' 41 | return self.list_resources(uri, **filters) 42 | 43 | def is_resource_deleted(self, id): 44 | try: 45 | self.show_flowclassifier(id) 46 | except lib_exc.NotFound: 47 | return True 48 | return False 49 | 50 | @property 51 | def resource_type(self): 52 | """Returns the primary type of resource this client works with.""" 53 | return 'flow_classifier' 54 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/test_basic.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | import testtools 16 | 17 | from tempest.lib import decorators 18 | 19 | from neutron_tempest_plugin.common import ssh 20 | from neutron_tempest_plugin import config 21 | from neutron_tempest_plugin.scenario import base 22 | 23 | CONF = config.CONF 24 | 25 | 26 | class NetworkBasicTest(base.BaseTempestTestCase): 27 | credentials = ['primary', 'admin'] 28 | force_tenant_isolation = False 29 | 30 | # Default to ipv4. 31 | _ip_version = 4 32 | 33 | @decorators.idempotent_id('de07fe0a-e955-449e-b48b-8641c14cd52e') 34 | def test_basic_instance(self): 35 | self.setup_network_and_server() 36 | self.check_connectivity(self.fip['floating_ip_address'], 37 | CONF.validation.image_ssh_user, 38 | self.keypair['private_key']) 39 | 40 | @testtools.skipUnless( 41 | CONF.neutron_plugin_options.global_ip_address, 42 | 'Global IP address is not defined.') 43 | @decorators.idempotent_id('49609189-3a0e-43c7-832e-a7e114aad1c9') 44 | def test_ping_global_ip_from_vm_with_fip(self): 45 | self.setup_network_and_server() 46 | server_ssh_client = ssh.Client(self.fip['floating_ip_address'], 47 | CONF.validation.image_ssh_user, 48 | pkey=self.keypair['private_key']) 49 | self.check_remote_connectivity( 50 | server_ssh_client, 51 | CONF.neutron_plugin_options.global_ip_address, 52 | ping_count=1) 53 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 3.18.0 3 | envlist = pep8 4 | 5 | [testenv] 6 | usedevelop = True 7 | setenv = 8 | VIRTUAL_ENV={envdir} 9 | PYTHONWARNINGS=default::DeprecationWarning 10 | OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true} 11 | OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true} 12 | OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true} 13 | deps = 14 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 15 | -r{toxinidir}/requirements.txt 16 | -r{toxinidir}/test-requirements.txt 17 | commands = stestr run --slowest {posargs} 18 | 19 | [testenv:pep8] 20 | commands = 21 | bash ./tools/misc-sanity-checks.sh 22 | flake8 23 | allowlist_externals = 24 | bash 25 | 26 | [testenv:venv] 27 | commands = {posargs} 28 | 29 | [testenv:docs] 30 | deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 31 | -r{toxinidir}/requirements.txt 32 | -r{toxinidir}/doc/requirements.txt 33 | commands = 34 | rm -rf doc/source/tests 35 | sphinx-apidoc -f -o doc/source/tests neutron_tempest_plugin 36 | rm -rf doc/build 37 | sphinx-build -W -b html doc/source doc/build/html 38 | allowlist_externals = 39 | rm 40 | 41 | [testenv:releasenotes] 42 | deps = {[testenv:docs]deps} 43 | commands = 44 | sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html 45 | 46 | [testenv:debug] 47 | commands = oslo_debug_helper -t neutron_tempest_plugin/ {posargs} 48 | 49 | [flake8] 50 | # E126 continuation line over-indented for hanging indent 51 | # E128 continuation line under-indented for visual indent 52 | # E129 visually indented line with same indent as next logical line 53 | # I202 Additional newline in a group of imports. 54 | # N530 direct neutron imports not allowed 55 | # N535 prevent eventlet library import 56 | # W504 line break after binary operator 57 | ignore = E126,E128,E129,I202,N530,W504 58 | # H106: Don't put vim configuration in source files 59 | # H203: Use assertIs(Not)None to check for None 60 | # H204: Use assert(Not)Equal to check for equality 61 | # H205: Use assert(Greater|Less)(Equal) for comparison 62 | # H904: Delay string interpolations at logging calls 63 | enable-extensions = H106,H203,H204,H205,H904 64 | show-source = true 65 | exclude = ./.*,build,dist,doc,*egg*,releasenotes 66 | import-order-style = pep8 67 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_agent_availability_zone.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | from neutron_lib import constants 13 | from tempest.lib import decorators 14 | import testtools 15 | 16 | from neutron_tempest_plugin.api import base 17 | from neutron_tempest_plugin import config 18 | 19 | AZ_SUPPORTED_AGENTS = [constants.AGENT_TYPE_DHCP, constants.AGENT_TYPE_L3] 20 | CONF = config.CONF 21 | 22 | 23 | class AgentAvailabilityZoneTestCase(base.BaseAdminNetworkTest): 24 | 25 | required_extensions = ['agent', 'availability_zone'] 26 | 27 | @classmethod 28 | def resource_setup(cls): 29 | super(AgentAvailabilityZoneTestCase, cls).resource_setup() 30 | body = cls.admin_client.list_agents() 31 | agents = body['agents'] 32 | agents_type = [agent.get('agent_type') for agent in agents] 33 | for az_agent in AZ_SUPPORTED_AGENTS: 34 | if az_agent in agents_type: 35 | return 36 | msg = 'availability_zone supported agent not found.' 37 | raise cls.skipException(msg) 38 | 39 | @decorators.idempotent_id('3ffa661e-cfcc-417d-8b63-1c5ec4a22e54') 40 | @testtools.skipUnless(CONF.neutron_plugin_options.agent_availability_zone, 41 | "Need a single availability_zone assumption.") 42 | def test_agents_availability_zone(self): 43 | """Test list agents availability_zone 44 | 45 | Only L3 and DHCP agent support availability_zone, default 46 | availability_zone is "nova". 47 | """ 48 | body = self.admin_client.list_agents() 49 | agents = body['agents'] 50 | for agent in agents: 51 | if agent.get('agent_type') in AZ_SUPPORTED_AGENTS: 52 | self.assertEqual( 53 | CONF.neutron_plugin_options.agent_availability_zone, 54 | agent.get('availability_zone')) 55 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_extensions.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from tempest.common import utils 14 | from tempest import config 15 | from tempest.lib import decorators 16 | 17 | from neutron_tempest_plugin.api import base 18 | 19 | 20 | CONF = config.CONF 21 | 22 | 23 | class ExtensionsTest(base.BaseNetworkTest): 24 | 25 | def _test_list_extensions_includes(self, exts): 26 | body = self.client.list_extensions() 27 | extensions = {ext_['alias'] for ext_ in body['extensions']} 28 | self.assertNotEmpty(extensions, "Extension list returned is empty") 29 | for ext in exts: 30 | ext_enabled = utils.is_extension_enabled(ext, "network") 31 | if ext_enabled: 32 | self.assertIn(ext, extensions) 33 | else: 34 | self.assertNotIn(ext, extensions) 35 | 36 | @decorators.idempotent_id('262420b7-a4bb-4a3e-b4b5-e73bad18df8c') 37 | def test_list_extensions_sorting(self): 38 | self._test_list_extensions_includes(['sorting']) 39 | 40 | @decorators.idempotent_id('19db409e-a23f-445d-8bc8-ca3d64c84706') 41 | def test_list_extensions_pagination(self): 42 | self._test_list_extensions_includes(['pagination']) 43 | 44 | @decorators.idempotent_id('155b7bc2-e358-4dd8-bf3e-1774c084567f') 45 | def test_list_extensions_project_id(self): 46 | self._test_list_extensions_includes(['project-id']) 47 | 48 | @decorators.idempotent_id('c7597fac-2404-45b1-beb4-523c8b1d4604') 49 | def test_list_extensions_includes_all(self): 50 | extensions = CONF.network_feature_enabled.api_extensions 51 | if not extensions: 52 | raise self.skipException("Extension list is empty") 53 | if extensions[0] == 'all': 54 | raise self.skipException("No lists of enabled extensions provided") 55 | 56 | self._test_list_extensions_includes(extensions) 57 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_logging_negative.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from oslo_utils import uuidutils 14 | from tempest.lib.common.utils import data_utils 15 | from tempest.lib import decorators 16 | from tempest.lib import exceptions as lib_exc 17 | 18 | from neutron_tempest_plugin.api import base 19 | 20 | 21 | class LoggingNegativeTestJSON(base.BaseAdminNetworkTest): 22 | 23 | required_extensions = ['logging', 'standard-attr-description'] 24 | 25 | @decorators.attr(type='negative') 26 | @decorators.idempotent_id('5fc61e24-cad5-4d86-a2d4-f40c0fa0a54c') 27 | def test_create_log_with_invalid_resource_type(self): 28 | log_args = {'name': data_utils.rand_name('test-log'), 29 | 'description': data_utils.rand_name('test-log-desc'), 30 | 'resource_type': 'fake_resource'} 31 | self.assertRaises(lib_exc.BadRequest, 32 | self.admin_client.create_log, **log_args) 33 | 34 | @decorators.attr(type='negative') 35 | @decorators.idempotent_id('7ed63170-0748-44b7-b0a0-64bfd9390dac') 36 | def test_create_log_with_nonexistent_port(self): 37 | log_args = {'name': data_utils.rand_name('test-log'), 38 | 'description': data_utils.rand_name('test-log-desc'), 39 | 'resource_type': 'security_group', 40 | 'target_id': uuidutils.generate_uuid()} 41 | self.assertRaises(lib_exc.NotFound, 42 | self.admin_client.create_log, **log_args) 43 | 44 | @decorators.attr(type='negative') 45 | @decorators.idempotent_id('89194c6b-8f47-400b-979b-072b1c1f767b') 46 | def test_create_log_with_nonexistent_sg(self): 47 | log_args = {'name': data_utils.rand_name('test-log'), 48 | 'description': data_utils.rand_name('test-log-desc'), 49 | 'resource_type': 'security_group', 50 | 'resource_id': uuidutils.generate_uuid()} 51 | self.assertRaises(lib_exc.NotFound, 52 | self.admin_client.create_log, **log_args) 53 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/base_routers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib import exceptions 17 | 18 | from neutron_tempest_plugin.api import base 19 | 20 | 21 | class BaseRouterTest(base.BaseAdminNetworkTest): 22 | # NOTE(salv-orlando): This class inherits from BaseAdminNetworkTest 23 | # as some router operations, such as enabling or disabling SNAT 24 | # require admin credentials by default 25 | 26 | def _cleanup_router(self, router, client=None): 27 | try: 28 | self.delete_router(router, client) 29 | self.routers.remove(router) 30 | except exceptions.NotFound: 31 | pass 32 | 33 | def _create_router(self, name, admin_state_up=False, 34 | external_network_id=None, enable_snat=None, 35 | client=None, **kwargs): 36 | # associate a cleanup with created routers to avoid quota limits 37 | client = client or self.client 38 | router = self._create_router_with_client( 39 | client, router_name=name, admin_state_up=admin_state_up, 40 | external_network_id=external_network_id, enable_snat=enable_snat, 41 | **kwargs) 42 | self.addCleanup(self._cleanup_router, router) 43 | return router 44 | 45 | def _create_admin_router(self, *args, **kwargs): 46 | router = self.create_admin_router(*args, **kwargs) 47 | self.addCleanup( 48 | self._cleanup_router, router, self.os_admin.network_client) 49 | return router 50 | 51 | def _delete_router(self, router_id, network_client=None): 52 | client = network_client or self.client 53 | client.delete_router(router_id) 54 | # Asserting that the router is not found in the list 55 | # after deletion 56 | list_body = self.client.list_routers() 57 | routers_list = list() 58 | for router in list_body['routers']: 59 | routers_list.append(router['id']) 60 | self.assertNotIn(router_id, routers_list) 61 | -------------------------------------------------------------------------------- /zuul.d/base-nested-switch.yaml: -------------------------------------------------------------------------------- 1 | - nodeset: 2 | name: neutron-nested-virt-ubuntu-focal 3 | nodes: 4 | - name: controller 5 | label: nested-virt-ubuntu-focal 6 | groups: 7 | - name: tempest 8 | nodes: 9 | - controller 10 | 11 | - nodeset: 12 | name: neutron-nested-virt-ubuntu-jammy 13 | nodes: 14 | - name: controller 15 | label: nested-virt-ubuntu-jammy 16 | groups: 17 | - name: tempest 18 | nodes: 19 | - controller 20 | 21 | - nodeset: 22 | name: neutron-nested-virt-ubuntu-noble 23 | nodes: 24 | - name: controller 25 | label: nested-virt-ubuntu-noble 26 | groups: 27 | - name: tempest 28 | nodes: 29 | - controller 30 | 31 | # Base nested switch job for 2025.1 and later 32 | - job: 33 | name: neutron-tempest-plugin-base-nested-switch 34 | parent: neutron-tempest-plugin-base 35 | abstract: true 36 | branches: 37 | regex: ^(unmaintained|stable/(victoria|wallaby|xena|yoga|zed|2023|2024)).*$ 38 | negate: true 39 | # Comment nodeset and vars to switch back to non nested nodes 40 | nodeset: neutron-nested-virt-ubuntu-noble 41 | vars: &nested_virt_vars 42 | devstack_localrc: 43 | LIBVIRT_TYPE: kvm 44 | # NOTE(ykarel): seeing issues with host-passthrough mode 45 | # https://bugs.launchpad.net/neutron/+bug/2036603 46 | # LIBVIRT_CPU_MODE: host-passthrough 47 | # TODO(ykarel): switch to 0.6.2+ once lp#2045549 is fixed 48 | CIRROS_VERSION: 0.5.3 49 | DEFAULT_IMAGE_NAME: cirros-0.5.3-x86_64-disk 50 | DEFAULT_IMAGE_FILE_NAME: cirros-0.5.3-x86_64-disk.img 51 | 52 | # Base nested switch job for 2023.1 till 2024.2 53 | - job: 54 | name: neutron-tempest-plugin-base-nested-switch 55 | parent: neutron-tempest-plugin-base 56 | abstract: true 57 | branches: ^(unmaintained|stable)/(2023|2024).*$ 58 | # Comment nodeset and vars to switch back to non nested nodes 59 | nodeset: neutron-nested-virt-ubuntu-jammy 60 | vars: *nested_virt_vars 61 | 62 | # Base nested switch job for yoga and zed 63 | - job: 64 | name: neutron-tempest-plugin-base-nested-switch 65 | parent: neutron-tempest-plugin-base 66 | abstract: true 67 | branches: ^(unmaintained|stable)/(yoga|zed)$ 68 | # Comment nodeset and vars to switch back to non nested nodes 69 | nodeset: neutron-nested-virt-ubuntu-focal 70 | vars: *nested_virt_vars 71 | 72 | # Base nested switch job for EM releases 73 | - job: 74 | name: neutron-tempest-plugin-base-nested-switch 75 | parent: neutron-tempest-plugin-base 76 | abstract: true 77 | branches: ^(stable/(victoria|wallaby|xena)).*$ 78 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/scenario/fwaas_v2_manager.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 OpenStack Foundation 2 | # Copyright 2013 IBM Corp. 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from oslo_log import log 18 | 19 | from tempest import config 20 | from tempest.lib.common.utils import data_utils 21 | from tempest.lib.common.utils import test_utils 22 | from tempest.scenario import manager 23 | 24 | CONF = config.CONF 25 | 26 | LOG = log.getLogger(__name__) 27 | 28 | 29 | class ScenarioTest(manager.NetworkScenarioTest): 30 | """Base class for scenario tests. Uses tempest own clients. """ 31 | 32 | credentials = ['primary'] 33 | 34 | 35 | class NetworkScenarioTest(ScenarioTest): 36 | """Base class for network scenario tests. 37 | 38 | This class provide helpers for network scenario tests, using the neutron 39 | API. Helpers from ancestor which use the nova network API are overridden 40 | with the neutron API. 41 | 42 | This Class also enforces using Neutron instead of novanetwork. 43 | Subclassed tests will be skipped if Neutron is not enabled 44 | 45 | """ 46 | 47 | credentials = ['primary', 'admin'] 48 | 49 | @classmethod 50 | def skip_checks(cls): 51 | super(NetworkScenarioTest, cls).skip_checks() 52 | if not CONF.service_available.neutron: 53 | raise cls.skipException('Neutron not available') 54 | 55 | def _create_router(self, client=None, tenant_id=None, 56 | namestart='router-smoke'): 57 | if not client: 58 | client = self.routers_client 59 | if not tenant_id: 60 | tenant_id = client.project_id 61 | name = data_utils.rand_name(namestart) 62 | result = client.create_router(name=name, 63 | admin_state_up=True, 64 | tenant_id=tenant_id) 65 | router = result['router'] 66 | self.assertEqual(router['name'], name) 67 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 68 | client.delete_router, 69 | router['id']) 70 | return router 71 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/vpnaas/services/clients_vpnaas.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 OpenStack Foundation 2 | # Copyright 2016 Hewlett Packard Enterprise Development Company 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from neutron_tempest_plugin.api import clients as manager 18 | from neutron_tempest_plugin import config 19 | from neutron_tempest_plugin.services.network.json import network_client 20 | 21 | 22 | CONF = config.CONF 23 | 24 | 25 | class NetworkClient(network_client.NetworkClientJSON): 26 | 27 | def pluralize(self, resource_name): 28 | 29 | resource_plural_map = { 30 | 'ikepolicy': 'ikepolicies', 31 | 'ipsecpolicy': 'ipsecpolicies' 32 | } 33 | 34 | if resource_name in resource_plural_map: 35 | return resource_plural_map.get(resource_name) 36 | 37 | return super(NetworkClient, self).pluralize(resource_name) 38 | 39 | def get_uri(self, plural_name): 40 | # get service prefix from resource name 41 | 42 | service_resource_prefix_list = [ 43 | 'vpnservices', 44 | 'ikepolicies', 45 | 'ipsecpolicies', 46 | 'ipsec_site_connections', 47 | 'endpoint_groups', 48 | ] 49 | 50 | if plural_name in service_resource_prefix_list: 51 | plural_name = plural_name.replace("_", "-") 52 | service_prefix = 'vpn' 53 | uri = '%s/%s/%s' % (self.uri_prefix, service_prefix, 54 | plural_name) 55 | return uri 56 | 57 | return super(NetworkClient, self).get_uri(plural_name) 58 | 59 | 60 | class Manager(manager.Manager): 61 | def __init__(self, credentials=None, service=None): 62 | super(Manager, self).__init__(credentials=credentials) 63 | self.network_client = NetworkClient( 64 | self.auth_provider, 65 | CONF.network.catalog_type, 66 | CONF.network.region or CONF.identity.region, 67 | endpoint_type=CONF.network.endpoint_type, 68 | build_interval=CONF.network.build_interval, 69 | build_timeout=CONF.network.build_timeout, 70 | **self.default_params) 71 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/plugin.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | import os 18 | 19 | from tempest import config 20 | from tempest.test_discover import plugins 21 | 22 | from neutron_tempest_plugin import config as neutron_config 23 | 24 | 25 | class NeutronTempestPlugin(plugins.TempestPlugin): 26 | def load_tests(self): 27 | base_path = os.path.split(os.path.dirname( 28 | os.path.abspath(__file__)))[0] 29 | test_dir = "neutron_tempest_plugin" 30 | full_test_dir = os.path.join(base_path, test_dir) 31 | return full_test_dir, base_path 32 | 33 | def register_opts(self, conf): 34 | config.register_opt_group(conf, neutron_config.neutron_group, 35 | neutron_config.NeutronPluginOptions) 36 | config.register_opt_group(conf, neutron_config.bgpvpn_group, 37 | neutron_config.BgpvpnGroup) 38 | config.register_opt_group(conf, neutron_config.fwaas_group, 39 | neutron_config.FwaasGroup) 40 | config.register_opt_group(conf, neutron_config.taas_group, 41 | neutron_config.TaasGroup) 42 | config.register_opt_group(conf, neutron_config.dynamic_routing_group, 43 | neutron_config.DynamicRoutingGroup) 44 | config.register_opt_group(conf, neutron_config.dns_feature_group, 45 | neutron_config.DnsFeatureGroup) 46 | 47 | def get_opt_lists(self): 48 | return [ 49 | (neutron_config.neutron_group.name, 50 | neutron_config.NeutronPluginOptions), 51 | (neutron_config.bgpvpn_group.name, 52 | neutron_config.BgpvpnGroup), 53 | (neutron_config.fwaas_group.name, 54 | neutron_config.FwaasGroup), 55 | (neutron_config.taas_group.name, 56 | neutron_config.TaasGroup), 57 | (neutron_config.dynamic_routing_group.name, 58 | neutron_config.DynamicRoutingGroup), 59 | (neutron_config.dns_feature_group.name, 60 | neutron_config.DnsFeatureGroup) 61 | ] 62 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_floating_ips_negative.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard Development Company, L.P. 2 | # Copyright 2014 OpenStack Foundation 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from tempest.lib.common.utils import data_utils 18 | from tempest.lib import decorators 19 | from tempest.lib import exceptions as lib_exc 20 | 21 | from neutron_tempest_plugin.api import base 22 | from neutron_tempest_plugin import config 23 | 24 | CONF = config.CONF 25 | 26 | 27 | class FloatingIPNegativeTestJSON(base.BaseNetworkTest): 28 | 29 | required_extensions = ['router'] 30 | 31 | @classmethod 32 | def resource_setup(cls): 33 | super(FloatingIPNegativeTestJSON, cls).resource_setup() 34 | cls.ext_net_id = CONF.network.public_network_id 35 | # Create a network with a subnet connected to a router. 36 | cls.network = cls.create_network() 37 | cls.subnet = cls.create_subnet(cls.network) 38 | cls.router = cls.create_router(data_utils.rand_name('router')) 39 | cls.create_router_interface(cls.router['id'], cls.subnet['id']) 40 | cls.port = cls.create_port(cls.network) 41 | 42 | @decorators.attr(type='negative') 43 | @decorators.idempotent_id('0b5b8797-6de7-4191-905c-a48b888eb429') 44 | def test_associate_floatingip_with_port_with_floatingip(self): 45 | net = self.create_network() 46 | subnet = self.create_subnet(net) 47 | r = self.create_router('test') 48 | self.create_router_interface(r['id'], subnet['id']) 49 | self.client.update_router( 50 | r['id'], 51 | external_gateway_info={ 52 | 'network_id': self.ext_net_id}) 53 | self.addCleanup(self.client.update_router, self.router['id'], 54 | external_gateway_info={}) 55 | port = self.create_port(net) 56 | floating_ip1 = self.create_floatingip() 57 | floating_ip2 = self.create_floatingip() 58 | self.client.update_floatingip(floating_ip1['id'], 59 | port_id=port['id']) 60 | self.assertRaises(lib_exc.Conflict, self.client.update_floatingip, 61 | floating_ip2['id'], port_id=port['id']) 62 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/scenario/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Futurewei. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from oslo_log import log as logging 16 | 17 | from tempest import config 18 | from tempest.lib import exceptions as lib_exc 19 | 20 | from neutron_tempest_plugin.sfc.tests import flowclassifier_client 21 | from neutron_tempest_plugin.sfc.tests.scenario import manager 22 | from neutron_tempest_plugin.sfc.tests import sfc_client 23 | 24 | CONF = config.CONF 25 | LOG = logging.getLogger(__name__) 26 | 27 | 28 | class SfcScenarioTest( 29 | flowclassifier_client.FlowClassifierClientMixin, 30 | sfc_client.SfcClientMixin, 31 | manager.NetworkScenarioTest 32 | ): 33 | 34 | def _check_connectivity( 35 | self, source_ip, destination_ip, routes=None, 36 | username=None, private_key=None 37 | ): 38 | msg = "ip address %r is reachable" % source_ip 39 | ok = self.ping_ip_address(source_ip, should_succeed=True) 40 | self.assertTrue(ok, msg=msg) 41 | client = self.get_remote_client( 42 | source_ip, username=username, private_key=private_key) 43 | cmd = 'traceroute -n -I %s' % destination_ip 44 | LOG.debug('exec command on %s: %s', source_ip, cmd) 45 | try: 46 | result = client.exec_command(cmd) 47 | LOG.debug( 48 | 'traceroute from %s to %s:\n%s', 49 | source_ip, destination_ip, result) 50 | lines = result.split('\n') 51 | lines = [line for line in lines if line] 52 | lines = lines[1:-1] 53 | if len(lines) != len(routes): 54 | LOG.error('length mismatch:\n%s\nvs\n%s', lines, routes) 55 | self.assertEqual(len(lines), len(routes)) 56 | for line, route_list in zip(lines, routes): 57 | found = any([route in line for route in route_list]) 58 | if not found: 59 | LOG.error('did not found any route %s in %s', 60 | route_list, line) 61 | self.assertTrue(found) 62 | except lib_exc.SSHExecCommandFailed as e: 63 | LOG.exception(e) 64 | raise 65 | except lib_exc.SSHTimeout as e: 66 | LOG.exception(e) 67 | raise 68 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/test_dvr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from tempest.common import utils 16 | from tempest.lib import decorators 17 | import testtools 18 | 19 | from neutron_lib import constants 20 | from neutron_tempest_plugin import config 21 | from neutron_tempest_plugin.scenario import base 22 | 23 | CONF = config.CONF 24 | 25 | 26 | class NetworkTestMixin(object): 27 | def _check_connectivity(self): 28 | self.check_connectivity(self.fip['floating_ip_address'], 29 | CONF.validation.image_ssh_user, 30 | self.keypair['private_key']) 31 | 32 | def _check_snat_port_connectivity(self): 33 | self._check_connectivity() 34 | 35 | # Put the Router_SNAT port down to make sure the traffic flows through 36 | # Compute node. 37 | self._put_snat_port_down(self.network['id']) 38 | self._check_connectivity() 39 | 40 | def _put_snat_port_down(self, network_id): 41 | port_id = self.client.list_ports( 42 | network_id=network_id, 43 | device_owner=constants.DEVICE_OWNER_ROUTER_SNAT)['ports'][0]['id'] 44 | self.os_admin.network_client.update_port( 45 | port_id, admin_state_up=False) 46 | 47 | 48 | class NetworkDvrTest(base.BaseTempestTestCase, NetworkTestMixin): 49 | credentials = ['primary', 'admin'] 50 | force_tenant_isolation = False 51 | 52 | @classmethod 53 | @utils.requires_ext(extension="dvr", service="network") 54 | def skip_checks(cls): 55 | super(NetworkDvrTest, cls).skip_checks() 56 | 57 | @decorators.idempotent_id('3d73ec1a-2ec6-45a9-b0f8-04a283d9d344') 58 | @testtools.skipUnless( 59 | CONF.neutron_plugin_options.l3_agent_mode == 'dvr_snat', 60 | "Need dvr_snat agent mode assumption.") 61 | def test_vm_reachable_through_compute(self): 62 | """Check that the VM is reachable through compute node. 63 | 64 | The test is done by putting the SNAT port down on controller node. 65 | """ 66 | router = self.create_router_by_client( 67 | distributed=True, tenant_id=self.client.project_id, is_admin=True, 68 | ha=False) 69 | self.setup_network_and_server(router=router) 70 | self._check_snat_port_connectivity() 71 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_trunk_details.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib import decorators 16 | 17 | from neutron_tempest_plugin.api import test_trunk 18 | 19 | 20 | class TestTrunkDetailsJSON(test_trunk.TrunkTestJSONBase): 21 | 22 | required_extensions = ['trunk-details'] 23 | 24 | @decorators.idempotent_id('f0bed24f-d36a-498b-b4e7-0d66e3fb7308') 25 | def test_port_resource_trunk_details_no_subports(self): 26 | trunk = self._create_trunk_with_network_and_parent() 27 | parent_port = self.client.show_port(trunk['port_id'])['port'] 28 | observed_trunk_details = parent_port.get('trunk_details') 29 | expected_trunk_details = {'sub_ports': [], 30 | 'trunk_id': trunk['id']} 31 | self.assertIsNotNone(observed_trunk_details) 32 | self.assertEqual(expected_trunk_details, observed_trunk_details) 33 | 34 | @decorators.idempotent_id('544bcaf2-86fb-4930-93ab-ece1c3cc33df') 35 | def test_port_resource_trunk_details_with_subport(self): 36 | subport_network = self.create_network() 37 | subport = self.create_port(subport_network) 38 | subport_data = {'port_id': subport['id'], 39 | 'segmentation_type': 'vlan', 40 | 'segmentation_id': 2} 41 | trunk = self._create_trunk_with_network_and_parent([subport_data]) 42 | parent_port = self.client.show_port(trunk['port_id'])['port'] 43 | observed_trunk_details = parent_port.get('trunk_details') 44 | expected_subport_data = dict(subport_data, 45 | mac_address=subport['mac_address']) 46 | expected_trunk_details = {'sub_ports': [expected_subport_data], 47 | 'trunk_id': trunk['id']} 48 | self.assertIsNotNone(observed_trunk_details) 49 | self.assertEqual(expected_trunk_details, observed_trunk_details) 50 | 51 | @decorators.idempotent_id('fe6d865f-1d5c-432e-b65d-904157172f24') 52 | def test_port_resource_empty_trunk_details(self): 53 | network = self.create_network() 54 | port = self.create_port(network) 55 | observed_port = self.client.show_port(port['id'])['port'] 56 | observed_trunk_details = observed_port.get('trunk_details') 57 | self.assertIsNone(observed_trunk_details) 58 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/scenario/fwaas_v2_base.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Midokura SARL 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest import config 17 | from tempest.lib.common import ssh 18 | from tempest.lib import exceptions as lib_exc 19 | 20 | from neutron_tempest_plugin.fwaas.common import fwaas_v2_client 21 | from neutron_tempest_plugin.fwaas.scenario import fwaas_v2_manager as manager 22 | 23 | CONF = config.CONF 24 | 25 | 26 | class FWaaSScenarioTestBase(object): 27 | def check_connectivity(self, ip_address, username=None, private_key=None, 28 | should_connect=True, 29 | check_icmp=True, check_ssh=True, 30 | check_reverse_icmp_ip=None, 31 | should_reverse_connect=True): 32 | if should_connect: 33 | msg = "Timed out waiting for %s to become reachable" % ip_address 34 | else: 35 | msg = "ip address %s is reachable" % ip_address 36 | if check_icmp: 37 | ok = self.ping_ip_address(ip_address, 38 | should_succeed=should_connect) 39 | self.assertTrue(ok, msg=msg) 40 | if check_ssh: 41 | connect_timeout = CONF.validation.connect_timeout 42 | kwargs = {} 43 | if not should_connect: 44 | # Use a shorter timeout for negative case 45 | kwargs['timeout'] = 1 46 | try: 47 | client = ssh.Client(ip_address, username, pkey=private_key, 48 | channel_timeout=connect_timeout, 49 | ssh_key_type=CONF.validation.ssh_key_type, 50 | **kwargs) 51 | client.test_connection_auth() 52 | self.assertTrue(should_connect, "Unexpectedly reachable") 53 | if check_reverse_icmp_ip: 54 | cmd = 'ping -c1 -w1 %s' % check_reverse_icmp_ip 55 | try: 56 | client.exec_command(cmd) 57 | self.assertTrue(should_reverse_connect, 58 | "Unexpectedly reachable (reverse)") 59 | except lib_exc.SSHExecCommandFailed: 60 | if should_reverse_connect: 61 | raise 62 | except lib_exc.SSHTimeout: 63 | if should_connect: 64 | raise 65 | 66 | 67 | class FWaaSScenarioTest_V2(fwaas_v2_client.FWaaSClientMixin, 68 | FWaaSScenarioTestBase, 69 | manager.NetworkScenarioTest): 70 | pass 71 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 | # implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | 18 | def autodoc_skip_member_handler(app, what, name, obj, skip, options): 19 | return skip or ( 20 | (what == "class" and not name.startswith("test")) or 21 | # NOTE(fnordahl): Sphinx does not like the ASCII art in the docstring. 22 | (what == 'module' and name == 'NetworkMultipleGWTest')) 23 | 24 | def setup(app): 25 | app.connect('autodoc-skip-member', autodoc_skip_member_handler) 26 | 27 | sys.path.insert(0, os.path.abspath('../../neutron_tempest_plugin')) 28 | 29 | # -- General configuration ---------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc', 35 | 'openstackdocstheme', 36 | ] 37 | 38 | # autodoc generation is a bit aggressive and a nuisance when doing heavy 39 | # text edit cycles. 40 | # execute "export SPHINX_DEBUG=1" in your terminal to disable 41 | 42 | # The suffix of source filenames. 43 | source_suffix = '.rst' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | copyright = '2017, OpenStack Developers' 50 | 51 | # openstackdocstheme options 52 | openstackdocs_repo_name = 'openstack/neutron-tempest-plugin' 53 | openstackdocs_bug_project = 'neutron_tempest_plugin' 54 | openstackdocs_bug_tag = '' 55 | 56 | # If true, '()' will be appended to :func: etc. cross-reference text. 57 | add_function_parentheses = True 58 | 59 | # If true, the current module name will be prepended to all description 60 | # unit titles (such as .. function::). 61 | add_module_names = True 62 | 63 | # The name of the Pygments (syntax highlighting) style to use. 64 | pygments_style = 'native' 65 | 66 | # -- Options for HTML output -------------------------------------------------- 67 | 68 | # The theme to use for HTML and HTML Help pages. Major themes that come with 69 | # Sphinx are currently 'default' and 'sphinxdoc'. 70 | # html_theme_path = ["."] 71 | # html_theme = '_theme' 72 | # html_static_path = ['static'] 73 | html_theme = 'openstackdocs' 74 | 75 | # Output file base name for HTML help builder. 76 | htmlhelp_basename = 'openstackdoc' 77 | 78 | # Grouping the document tree into LaTeX files. List of tuples 79 | # (source start file, target name, title, author, documentclass 80 | # [howto/manual]). 81 | latex_documents = [ 82 | ('index', 83 | 'openstack.tex', 84 | 'openstack Documentation', 85 | 'OpenStack Developers', 'manual'), 86 | ] 87 | 88 | # Example configuration for intersphinx: refer to the Python standard library. 89 | #intersphinx_mapping = {'http://docs.python.org/': None} 90 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/test_portsecurity.py: -------------------------------------------------------------------------------- 1 | # All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | import testtools 15 | 16 | from tempest.common import utils 17 | from tempest.lib import decorators 18 | 19 | from neutron_tempest_plugin import config 20 | from neutron_tempest_plugin.scenario import base 21 | 22 | CONF = config.CONF 23 | 24 | 25 | class PortSecurityTest(base.BaseTempestTestCase): 26 | credentials = ['primary', 'admin'] 27 | required_extensions = ['port-security'] 28 | 29 | def _test_port_security_removed_added(self, use_stateless_sg): 30 | """Test connection works after port security has been removed 31 | 32 | Initial test that vm is accessible. Then port security is removed, 33 | checked connectivity. Port security is added back and checked 34 | connectivity again. 35 | """ 36 | self.setup_network_and_server(use_stateless_sg=use_stateless_sg) 37 | self.check_connectivity(self.fip['floating_ip_address'], 38 | CONF.validation.image_ssh_user, 39 | self.keypair['private_key']) 40 | sec_group_id = self.security_groups[0]['id'] 41 | 42 | self.port = self.update_port(port=self.port, 43 | port_security_enabled=False, 44 | security_groups=[]) 45 | self.check_connectivity(self.fip['floating_ip_address'], 46 | CONF.validation.image_ssh_user, 47 | self.keypair['private_key']) 48 | 49 | self.port = self.update_port(port=self.port, 50 | port_security_enabled=True, 51 | security_groups=[sec_group_id]) 52 | self.check_connectivity(self.fip['floating_ip_address'], 53 | CONF.validation.image_ssh_user, 54 | self.keypair['private_key']) 55 | 56 | @decorators.idempotent_id('61ab176e-d48b-42b7-b38a-1ba571ecc033') 57 | def test_port_security_removed_added_stateful_sg(self): 58 | self._test_port_security_removed_added(use_stateless_sg=False) 59 | 60 | @decorators.idempotent_id('2f4005e1-cee1-40e5-adbd-b3e3cf218065') 61 | @testtools.skipUnless( 62 | utils.is_extension_enabled('stateful-security-group', 'network'), 63 | "'stateful-security-group' API extension not available") 64 | @testtools.skipIf( 65 | CONF.neutron_plugin_options.firewall_driver in ['openvswitch', 'None'], 66 | "Firewall driver other than 'openvswitch' is required to use " 67 | "stateless security groups.") 68 | def test_port_security_removed_added_stateless_sg(self): 69 | self._test_port_security_removed_added(use_stateless_sg=True) 70 | -------------------------------------------------------------------------------- /devstack/functions.sh: -------------------------------------------------------------------------------- 1 | # Generic use functions 2 | 3 | # ensure we don't re-source this in the same environment 4 | [[ -z "$_NEUTRON_TEMPEST_PLUGIN_FUNCTIONS" ]] || return 0 5 | declare -r -g _NEUTRON_TEMPEST_PLUGIN_FUNCTIONS=1 6 | 7 | # Create a function copying the code from an existing one 8 | function save_function { 9 | local old_name=$1 10 | local new_name=$2 11 | 12 | # Saving the same function again after redefining it could produce a 13 | # recorsive function in case for example this plugin is sourced twice 14 | if type -t "${new_name}"; then 15 | # Prevent copying the same function twice 16 | return 0 17 | fi 18 | 19 | # Save xtrace setting 20 | _XTRACE_FUNCTIONS=$(set +o | grep xtrace) 21 | set +o xtrace 22 | 23 | # Get code of the original function 24 | local old_code=$(declare -f ${old_name}) 25 | # Produce code for the new function 26 | local new_code="${new_name}${old_code#${old_name}}" 27 | # Define the new function 28 | eval "${new_code}" 29 | 30 | # Restore xtrace 31 | $_XTRACE_FUNCTIONS 32 | } 33 | 34 | #Add advanced image config to tempest.conf 35 | function configure_advanced_image { 36 | local advanced_image_uuid 37 | 38 | if ! is_service_enabled glance; then 39 | # if glance is not enabled, there is no image for to configure 40 | return 0 41 | fi 42 | 43 | if [[ -z "$ADVANCED_IMAGE_NAME" ]]; then 44 | # if name of advanced image is not provided, there is no image to 45 | # configure 46 | return 0 47 | fi 48 | 49 | while read -r IMAGE_NAME IMAGE_UUID; do 50 | if [ "$IMAGE_NAME" = "$ADVANCED_IMAGE_NAME" ]; then 51 | advanced_image_uuid="$IMAGE_UUID" 52 | break 53 | fi 54 | done < <(openstack image list --property status=active | awk -F'|' '!/^(+--)|ID|aki|ari/ { print $3,$2 }') 55 | 56 | if [[ -z "$advanced_image_uuid" ]]; then 57 | echo "No image with name $ADVANCED_IMAGE_NAME found." 58 | return 1 59 | fi 60 | 61 | iniset $TEMPEST_CONFIG neutron_plugin_options advanced_image_ref $advanced_image_uuid 62 | iniset $TEMPEST_CONFIG neutron_plugin_options advanced_image_ssh_user $ADVANCED_INSTANCE_USER 63 | } 64 | 65 | 66 | function configure_flavor_for_advanced_image { 67 | local flavor_ref 68 | 69 | if ! is_service_enabled nova; then 70 | # if nova is not enabled, there is no flavor to configure 71 | return 0 72 | fi 73 | 74 | if [[ -z "$ADVANCED_INSTANCE_TYPE" ]]; then 75 | # if name of flavor for advanced image is not provided, there is no 76 | # flavor to configure 77 | return 0 78 | fi 79 | 80 | flavor_ref=$(openstack flavor show $ADVANCED_INSTANCE_TYPE -f value -c id) 81 | if [[ -z "$flavor_ref" ]]; then 82 | echo "Found no valid flavors to use for $ADVANCED_IMAGE_NAME !" 83 | echo "Fallback to use $DEFAULT_INSTANCE_TYPE" 84 | flavor_ref=$(iniget $TEMPEST_CONFIG compute flavor_ref) 85 | fi 86 | iniset $TEMPEST_CONFIG neutron_plugin_options advanced_image_flavor_ref $flavor_ref 87 | } 88 | 89 | 90 | function create_flavor_for_advance_image { 91 | local name=$1 92 | local ram=$2 93 | local disk=$3 94 | local vcpus=$4 95 | 96 | if [[ -z $(openstack flavor list | grep $name) ]]; then 97 | openstack flavor create --ram $ram --disk $disk --vcpus $vcpus $name 98 | fi 99 | } 100 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/tap_as_a_service/services/taas_client.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from tempest.lib.services.network import base 14 | 15 | 16 | class TapServicesClient(base.BaseNetworkClient): 17 | 18 | def create_tap_service(self, **kwargs): 19 | uri = '/taas/tap_services' 20 | post_data = {'tap_service': kwargs} 21 | return self.create_resource(uri, post_data) 22 | 23 | def update_tap_service(self, tap_service_id, **kwargs): 24 | uri = '/taas/tap_services/%s' % tap_service_id 25 | post_data = {'tap_service': kwargs} 26 | return self.update_resource(uri, post_data) 27 | 28 | def show_tap_service(self, tap_service_id, **fields): 29 | uri = '/taas/tap_services/%s' % tap_service_id 30 | return self.show_resource(uri, **fields) 31 | 32 | def delete_tap_service(self, tap_service_id): 33 | uri = '/taas/tap_services/%s' % tap_service_id 34 | return self.delete_resource(uri) 35 | 36 | def list_tap_services(self, **filters): 37 | uri = '/taas/tap_services' 38 | return self.list_resources(uri, **filters) 39 | 40 | 41 | class TapFlowsClient(base.BaseNetworkClient): 42 | 43 | def create_tap_flow(self, **kwargs): 44 | uri = '/taas/tap_flows' 45 | post_data = {'tap_flow': kwargs} 46 | return self.create_resource(uri, post_data) 47 | 48 | def update_tap_flow(self, tap_flow_id, **kwargs): 49 | uri = '/taas/tap_flows/%s' % tap_flow_id 50 | post_data = {'tap_flow': kwargs} 51 | return self.update_resource(uri, post_data) 52 | 53 | def show_tap_flow(self, tap_flow_id, **fields): 54 | uri = '/taas/tap_flows/%s' % tap_flow_id 55 | return self.show_resource(uri, **fields) 56 | 57 | def delete_tap_flow(self, tap_flow_id): 58 | uri = '/taas/tap_flows/%s' % tap_flow_id 59 | return self.delete_resource(uri) 60 | 61 | def list_tap_flows(self, **filters): 62 | uri = '/taas/tap_flows' 63 | return self.list_resources(uri, **filters) 64 | 65 | 66 | class TapMirrorsClient(base.BaseNetworkClient): 67 | def create_tap_mirror(self, **kwargs): 68 | uri = '/taas/tap_mirrors' 69 | post_data = {'tap_mirror': kwargs} 70 | return self.create_resource(uri, post_data) 71 | 72 | def update_tap_mirror(self, tap_mirror_id, **kwargs): 73 | uri = '/taas/tap_mirrors/%s' % tap_mirror_id 74 | post_data = {'tap_mirror': kwargs} 75 | return self.update_resource(uri, post_data) 76 | 77 | def show_tap_mirror(self, tap_mirror_id, **fields): 78 | uri = '/taas/tap_mirrors/%s' % tap_mirror_id 79 | return self.show_resource(uri, **fields) 80 | 81 | def delete_tap_mirror(self, tap_mirror_id): 82 | uri = '/taas/tap_mirrors/%s' % tap_mirror_id 83 | return self.delete_resource(uri) 84 | 85 | def list_tap_mirrors(self, **filters): 86 | uri = '/taas/tap_mirrors' 87 | return self.list_resources(uri, **filters) 88 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_ports_negative.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from neutron_lib.db import constants as db_const 14 | from oslo_utils import uuidutils 15 | from tempest.lib import decorators 16 | from tempest.lib import exceptions as lib_exc 17 | 18 | from neutron_tempest_plugin.api import base 19 | 20 | LONG_NAME_NG = 'z' * (db_const.NAME_FIELD_SIZE + 1) 21 | LONG_DESCRIPTION_NG = 'z' * (db_const.LONG_DESCRIPTION_FIELD_SIZE + 1) 22 | 23 | 24 | class PortsNegativeTestJSON(base.BaseNetworkTest): 25 | 26 | @classmethod 27 | def resource_setup(cls): 28 | super(PortsNegativeTestJSON, cls).resource_setup() 29 | cls.network = cls.create_network() 30 | 31 | @decorators.attr(type='negative') 32 | @decorators.idempotent_id('0cbd256a-a6d4-4afa-a039-44cc13704bab') 33 | def test_add_port_with_too_long_name(self): 34 | self.assertRaises(lib_exc.BadRequest, 35 | self.create_port, 36 | self.network, name=LONG_NAME_NG) 37 | 38 | @decorators.attr(type='negative') 39 | @decorators.idempotent_id('e10da38c-1071-49c9-95c2-0c451e18ae31') 40 | def test_add_port_with_too_long_description(self): 41 | self.assertRaises(lib_exc.BadRequest, 42 | self.create_port, 43 | self.network, description=LONG_DESCRIPTION_NG) 44 | 45 | @decorators.attr(type='negative') 46 | @decorators.idempotent_id('5b69a905-3a84-43a4-807a-1a67ab85caeb') 47 | def test_add_port_with_nonexist_tenant_id(self): 48 | self.assertRaises(lib_exc.BadRequest, 49 | self.create_port, 50 | self.network, 51 | project_id=uuidutils.generate_uuid()) 52 | 53 | @decorators.attr(type='negative') 54 | @decorators.idempotent_id('7cf473ae-7ec8-4834-ae17-9ef6ec6b8a32') 55 | def test_add_port_with_nonexist_network_id(self): 56 | network = self.network 57 | # Copy and restore net ID so the cleanup will delete correct net 58 | original_network_id = network['id'] 59 | network['id'] = uuidutils.generate_uuid() 60 | self.assertRaises(lib_exc.NotFound, 61 | self.create_port, 62 | network) 63 | network['id'] = original_network_id 64 | 65 | @decorators.attr(type='negative') 66 | @decorators.idempotent_id('cad2d349-25fa-490e-9675-cd2ea24164bc') 67 | def test_add_port_with_nonexist_security_groups_id(self): 68 | self.assertRaises(lib_exc.NotFound, 69 | self.create_port, 70 | self.network, 71 | security_groups=[uuidutils.generate_uuid()]) 72 | 73 | @decorators.attr(type='negative') 74 | @decorators.idempotent_id('9b0a4152-9aa4-4169-9b2c-579609e2fb4a') 75 | def test_add_port_with_illegal_ip(self): 76 | self.assertRaises(lib_exc.BadRequest, 77 | self.create_port, 78 | self.network, 79 | allowed_address_pairs=[{"ip_address: 12.12.12.a"}]) 80 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib import exceptions 17 | 18 | from neutron_tempest_plugin.common import utils 19 | 20 | 21 | class NeutronTempestPluginException(exceptions.TempestException): 22 | 23 | def __init__(self, **kwargs): 24 | super(NeutronTempestPluginException, self).__init__(**kwargs) 25 | self._properties = kwargs 26 | 27 | def __getattr__(self, name): 28 | try: 29 | return self._properties[name] 30 | except KeyError: 31 | pass 32 | 33 | msg = ("AttributeError: {!r} object has no attribute {!r}").format( 34 | self, name) 35 | raise AttributeError(msg) 36 | 37 | 38 | class InvalidConfiguration(NeutronTempestPluginException): 39 | message = "Invalid Configuration" 40 | 41 | 42 | class InvalidCredentials(NeutronTempestPluginException): 43 | message = "Invalid Credentials" 44 | 45 | 46 | class InvalidServiceTag(NeutronTempestPluginException): 47 | message = "Invalid service tag" 48 | 49 | 50 | class SSHScriptException(exceptions.TempestException): 51 | """Base class for SSH client execute_script() exceptions""" 52 | 53 | 54 | class ShellError(NeutronTempestPluginException): 55 | pass 56 | 57 | 58 | class ShellCommandFailed(ShellError): 59 | """Raised when shell command exited with non-zero status 60 | 61 | """ 62 | message = ("Command %(command)r failed, exit status: %(exit_status)d, " 63 | "stderr:\n%(stderr)s\n" 64 | "stdout:\n%(stdout)s") 65 | 66 | 67 | class SSHScriptFailed(ShellCommandFailed): 68 | message = ("Command %(command)r failed, exit status: %(exit_status)d, " 69 | "host: %(host)r\n" 70 | "script:\n%(script)s\n" 71 | "stderr:\n%(stderr)s\n" 72 | "stdout:\n%(stdout)s") 73 | 74 | 75 | class ShellTimeoutExpired(ShellError): 76 | """Raised when shell command timeouts and has been killed before exiting 77 | 78 | """ 79 | message = ("Command '%(command)s' timed out: %(timeout)d, " 80 | "stderr:\n%(stderr)s\n" 81 | "stdout:\n%(stdout)s") 82 | 83 | 84 | class SSHScriptTimeoutExpired(ShellTimeoutExpired): 85 | message = ("Command '%(command)s', timed out: %(timeout)d " 86 | "host: %(host)r\n" 87 | "script:\n%(script)s\n" 88 | "stderr:\n%(stderr)s\n" 89 | "stdout:\n%(stdout)s") 90 | 91 | 92 | # Patch SSHExecCommandFailed exception to make sure we can access to fields 93 | # command, exit_status, STDOUT and STDERR when SSH client reports command 94 | # failure 95 | exceptions.SSHExecCommandFailed = utils.override_class( 96 | exceptions.SSHExecCommandFailed, ShellCommandFailed) 97 | 98 | # Above code created a new SSHExecCommandFailed class based on top 99 | # of ShellCommandFailed 100 | assert issubclass(exceptions.SSHExecCommandFailed, ShellCommandFailed) 101 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/bgpvpn/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Ericsson. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import tempest.api.network.base as test 17 | from tempest.common import utils 18 | from tempest import config 19 | from tempest.lib.common.utils import data_utils 20 | 21 | from neutron_tempest_plugin.bgpvpn.services import bgpvpn_client 22 | 23 | CONF = config.CONF 24 | 25 | 26 | class BaseBgpvpnTest(test.BaseNetworkTest): 27 | """Base class for the Bgpvpn tests that use the Tempest Neutron REST client 28 | 29 | """ 30 | 31 | credentials = ['primary', 'admin', 'alt'] 32 | bgpvpn_client = None 33 | bgpvpn_admin_client = None 34 | bgpvpn_alt_client = None 35 | 36 | @classmethod 37 | def resource_cleanup(cls): 38 | for bgpvpn in cls.bgpvpns: 39 | cls.bgpvpn_admin_client.delete_bgpvpn(bgpvpn['id']) 40 | super(BaseBgpvpnTest, cls).resource_cleanup() 41 | 42 | @classmethod 43 | def resource_setup(cls): 44 | cls.route_distinguishers = [] 45 | cls.bgpvpns = [] 46 | cls.bgpvpn_client = bgpvpn_client.BgpvpnClient( 47 | cls.os_primary.auth_provider, 48 | CONF.network.catalog_type, 49 | CONF.network.region or CONF.identity.region, 50 | endpoint_type=CONF.network.endpoint_type, 51 | build_interval=CONF.network.build_interval, 52 | build_timeout=CONF.network.build_timeout, 53 | **cls.os_primary.default_params) 54 | cls.bgpvpn_admin_client = bgpvpn_client.BgpvpnClient( 55 | cls.os_admin.auth_provider, 56 | CONF.network.catalog_type, 57 | CONF.network.region or CONF.identity.region, 58 | endpoint_type=CONF.network.endpoint_type, 59 | build_interval=CONF.network.build_interval, 60 | build_timeout=CONF.network.build_timeout, 61 | **cls.os_admin.default_params) 62 | cls.bgpvpn_alt_client = bgpvpn_client.BgpvpnClient( 63 | cls.os_alt.auth_provider, 64 | CONF.network.catalog_type, 65 | CONF.network.region or CONF.identity.region, 66 | endpoint_type=CONF.network.endpoint_type, 67 | build_interval=CONF.network.build_interval, 68 | build_timeout=CONF.network.build_timeout, 69 | **cls.os_alt.default_params) 70 | super(BaseBgpvpnTest, cls).resource_setup() 71 | 72 | @classmethod 73 | def skip_checks(cls): 74 | super(BaseBgpvpnTest, cls).skip_checks() 75 | if not utils.is_extension_enabled('bgpvpn', 'network'): 76 | msg = "Bgpvpn extension not enabled." 77 | raise cls.skipException(msg) 78 | 79 | def create_bgpvpn(self, client, **kwargs): 80 | if 'name' not in kwargs: 81 | kwargs['name'] = data_utils.rand_name('test-bgpvpn-') 82 | 83 | body = client.create_bgpvpn(**kwargs) 84 | bgpvpn = body['bgpvpn'] 85 | self.bgpvpns.append(bgpvpn) 86 | return bgpvpn 87 | 88 | def delete_bgpvpn(self, client, bgpvpn): 89 | client.delete_bgpvpn(bgpvpn['id']) 90 | self.bgpvpns.remove(bgpvpn) 91 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_floating_ips_admin_actions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib.common.utils import data_utils 17 | from tempest.lib import decorators 18 | from tempest.lib import exceptions as lib_exc 19 | import testtools 20 | 21 | from neutron_tempest_plugin.api import base 22 | from neutron_tempest_plugin import config 23 | 24 | CONF = config.CONF 25 | 26 | 27 | class FloatingIPAdminTestJSON(base.BaseAdminNetworkTest): 28 | force_tenant_isolation = True 29 | credentials = ['primary', 'alt', 'admin'] 30 | 31 | @classmethod 32 | def resource_setup(cls): 33 | super(FloatingIPAdminTestJSON, cls).resource_setup() 34 | cls.ext_net_id = CONF.network.public_network_id 35 | cls.floating_ip = cls.create_floatingip() 36 | cls.alt_client = cls.os_alt.network_client 37 | cls.network = cls.create_network() 38 | cls.subnet = cls.create_subnet(cls.network) 39 | cls.router = cls.create_router(data_utils.rand_name('router'), 40 | external_network_id=cls.ext_net_id) 41 | cls.create_router_interface(cls.router['id'], cls.subnet['id']) 42 | cls.port = cls.create_port(cls.network) 43 | 44 | @decorators.attr(type='negative') 45 | @decorators.idempotent_id('11116ee9-4e99-5b15-b8e1-aa7df92ca589') 46 | def test_associate_floating_ip_with_port_from_another_project(self): 47 | floating_ip = self.create_floatingip() 48 | project_id = self.create_project()['id'] 49 | 50 | port = self.admin_client.create_port(network_id=self.network['id'], 51 | project_id=project_id) 52 | self.addCleanup(self.admin_client.delete_port, port['port']['id']) 53 | self.assertRaises(lib_exc.BadRequest, 54 | self.client.update_floatingip, 55 | floating_ip['id'], port_id=port['port']['id']) 56 | 57 | @testtools.skipUnless( 58 | CONF.neutron_plugin_options.specify_floating_ip_address_available, 59 | "Feature for specifying floating IP address is disabled") 60 | @decorators.idempotent_id('332a8ae4-402e-4b98-bb6f-532e5a87b8e0') 61 | def test_create_floatingip_with_specified_ip_address(self): 62 | # other tests may end up stealing the IP before we can use it 63 | # since it's on the external network so we need to retry if it's 64 | # in use. 65 | for _ in range(100): 66 | fip = self.get_unused_ip(self.ext_net_id, ip_version=4) 67 | try: 68 | created_floating_ip = self.create_floatingip( 69 | floating_ip_address=fip, 70 | client=self.admin_client) 71 | break 72 | except lib_exc.Conflict: 73 | pass 74 | else: 75 | self.fail("Could not get an unused IP after 100 attempts") 76 | self.assertIsNotNone(created_floating_ip['id']) 77 | self.assertIsNotNone(created_floating_ip['tenant_id']) 78 | self.assertEqual(created_floating_ip['floating_ip_address'], fip) 79 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/api/test_flowclassifier_extensions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Futurewei. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib.common.utils import data_utils 16 | from tempest.lib import decorators 17 | 18 | from neutron_tempest_plugin.sfc.tests.api import base 19 | 20 | 21 | class FlowClassifierExtensionTestJSON(base.BaseFlowClassifierTest): 22 | """Tests the following operations in the Neutron API: 23 | 24 | List flowclassifiers 25 | Create flowclassifier 26 | Update flowclassifier 27 | Delete flowclassifier 28 | Show flowclassifier 29 | """ 30 | 31 | @decorators.idempotent_id('1b84cf01-9c09-4ce7-bc72-b15e39076468') 32 | def test_list_flowclassifier(self): 33 | # List flow classifiers 34 | fc = self._try_create_flowclassifier() 35 | fcs = self.flowclassifier_client.list_flowclassifiers() 36 | self.assertIn(( 37 | fc['id'], 38 | fc['name'], 39 | fc['source_ip_prefix'], 40 | fc['logical_source_port'] 41 | ), [( 42 | m['id'], 43 | m['name'], 44 | m['source_ip_prefix'], 45 | m['logical_source_port'], 46 | ) for m in fcs['flow_classifiers']]) 47 | 48 | @decorators.idempotent_id('b2ed2a37-fc64-4be5-819b-9cf2a13db70b') 49 | def test_list_flowclassifier_with_logical_destination_port(self): 50 | # List flow classifiers with logical_destination_port 51 | fc = self._try_create_flowclassifier() 52 | fcs = self.flowclassifier_client.list_flowclassifiers() 53 | self.assertIn(( 54 | fc['id'], 55 | fc['name'], 56 | fc['source_ip_prefix'], 57 | fc['destination_ip_prefix'], 58 | fc['logical_source_port'], 59 | fc['logical_destination_port'] 60 | ), [( 61 | m['id'], 62 | m['name'], 63 | m['source_ip_prefix'], 64 | m['destination_ip_prefix'], 65 | m['logical_source_port'], 66 | m['logical_destination_port'] 67 | ) for m in fcs['flow_classifiers']]) 68 | 69 | @decorators.idempotent_id('563564f7-7077-4f5e-8cdc-51f37ae5a2b9') 70 | def test_update_flowclassifier(self): 71 | # Create flow classifier 72 | name1 = data_utils.rand_name('test') 73 | fc = self._try_create_flowclassifier( 74 | name=name1 75 | ) 76 | fc_id = fc['id'] 77 | 78 | # Update flow classifier 79 | name2 = data_utils.rand_name('test') 80 | body = self.flowclassifier_client.update_flowclassifier( 81 | fc_id, name=name2) 82 | self.assertEqual(body['flow_classifier']['name'], name2) 83 | 84 | @decorators.idempotent_id('3ff8c08e-26ff-4034-ae48-810ed213a998') 85 | def test_show_flowclassifier(self): 86 | # show a created flow classifier 87 | created = self._try_create_flowclassifier() 88 | fc = self.flowclassifier_client.show_flowclassifier( 89 | created['id']) 90 | for key, value in fc['flow_classifier'].items(): 91 | self.assertEqual(created[key], value) 92 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_networks_negative.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from tempest.lib import decorators 14 | from tempest.lib import exceptions as lib_exc 15 | import testtools 16 | 17 | from neutron_tempest_plugin.api import base 18 | from tempest import config 19 | 20 | CONF = config.CONF 21 | 22 | 23 | class NetworksNegativeTest(base.BaseNetworkTest): 24 | 25 | @classmethod 26 | def resource_setup(cls): 27 | super(NetworksNegativeTest, cls).resource_setup() 28 | cls.network = cls.create_network() 29 | cls.subnet = cls.create_subnet(cls.network) 30 | 31 | @decorators.attr(type='negative') 32 | @decorators.idempotent_id('9f80f25b-5d1b-4f26-9f6b-774b9b270819') 33 | def test_delete_network_in_use(self): 34 | self.create_port(self.network) 35 | with testtools.ExpectedException(lib_exc.Conflict): 36 | self.client.delete_subnet(self.subnet['id']) 37 | with testtools.ExpectedException(lib_exc.Conflict): 38 | self.client.delete_network(self.network['id']) 39 | 40 | @decorators.attr(type='negative') 41 | @decorators.idempotent_id('9f80f25b-5d1b-4f26-9f6b-774b9b270820') 42 | def test_update_network_mtu(self): 43 | with testtools.ExpectedException(lib_exc.BadRequest): 44 | self.client.create_network( 45 | mtu=CONF.neutron_plugin_options.max_mtu + 1) 46 | 47 | @decorators.attr(type='negative') 48 | @decorators.idempotent_id('53537bba-d6c3-4a2e-bda4-ab5b009fb7d9') 49 | def test_create_subnet_mtu_below_minimum_ipv4(self): 50 | network = self.create_network(mtu=67) 51 | with testtools.ExpectedException(lib_exc.Conflict): 52 | self.create_subnet(network, ip_version=4, cidr='10.0.0.0/24') 53 | 54 | @decorators.attr(type='negative') 55 | @decorators.idempotent_id('1de68cb6-e6d4-47df-b820-c5048796f33a') 56 | @testtools.skipUnless(config.CONF.network_feature_enabled.ipv6, 57 | 'IPv6 is not enabled') 58 | def test_create_subnet_mtu_below_minimum_ipv6(self): 59 | network = self.create_network(mtu=1279) 60 | with testtools.ExpectedException(lib_exc.Conflict): 61 | self.create_subnet(network, ip_version=6, cidr='2001:db8:0:1::/64') 62 | 63 | @decorators.attr(type='negative') 64 | @decorators.idempotent_id('5213df6d-7141-40b2-90ea-a958d9bc97e5') 65 | def test_update_network_mtu_below_minimum_ipv4(self): 66 | network = self.create_network(mtu=1280) 67 | self.create_subnet(network, ip_version=4, cidr='10.0.0.0/24') 68 | with testtools.ExpectedException(lib_exc.Conflict): 69 | self.client.update_network(network['id'], mtu=67) 70 | 71 | @decorators.attr(type='negative') 72 | @decorators.idempotent_id('1a714fc4-24b1-4c07-a005-d5c218672eab') 73 | @testtools.skipUnless(config.CONF.network_feature_enabled.ipv6, 74 | 'IPv6 is not enabled') 75 | def test_update_network_mtu_below_minimum_ipv6(self): 76 | network = self.create_network(mtu=1280) 77 | self.create_subnet(network, ip_version=6, cidr='2001:db8:0:1::/64') 78 | with testtools.ExpectedException(lib_exc.Conflict): 79 | self.client.update_network(network['id'], mtu=1279) 80 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_l3_agent_scheduler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 IBM Corp. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib.common.utils import data_utils 16 | from tempest.lib import decorators 17 | 18 | from neutron_tempest_plugin.api import base 19 | from neutron_tempest_plugin import exceptions 20 | 21 | AGENT_TYPE = 'L3 agent' 22 | AGENT_MODES = ( 23 | 'legacy', 24 | 'dvr_snat' 25 | ) 26 | 27 | 28 | class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest): 29 | _agent_mode = 'legacy' 30 | 31 | """ 32 | Tests the following operations in the Neutron API using the REST client for 33 | Neutron: 34 | 35 | List routers that the given L3 agent is hosting. 36 | List L3 agents hosting the given router. 37 | Add and Remove Router to L3 agent 38 | 39 | v2.0 of the Neutron API is assumed. 40 | 41 | The l3_agent_scheduler extension is required for these tests. 42 | """ 43 | 44 | required_extensions = ['l3_agent_scheduler'] 45 | 46 | @classmethod 47 | def resource_setup(cls): 48 | super(L3AgentSchedulerTestJSON, cls).resource_setup() 49 | body = cls.admin_client.list_agents() 50 | agents = body['agents'] 51 | for agent in agents: 52 | # TODO(armax): falling back on default _agent_mode can be 53 | # dropped as soon as Icehouse is dropped. 54 | agent_mode = ( 55 | agent['configurations'].get('agent_mode', cls._agent_mode)) 56 | if agent['agent_type'] == AGENT_TYPE and agent_mode in AGENT_MODES: 57 | cls.agent = agent 58 | break 59 | else: 60 | msg = "L3 Agent Scheduler enabled in conf, but L3 Agent not found" 61 | raise exceptions.InvalidConfiguration(msg) 62 | cls.router = cls.create_router(data_utils.rand_name('router')) 63 | 64 | @decorators.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a') 65 | def test_list_routers_on_l3_agent(self): 66 | self.admin_client.list_routers_on_l3_agent(self.agent['id']) 67 | 68 | @decorators.idempotent_id('9464e5e7-8625-49c3-8fd1-89c52be59d66') 69 | def test_add_list_remove_router_on_l3_agent(self): 70 | l3_agent_ids = list() 71 | 72 | # First list agents which host router 73 | body = self.admin_client.list_l3_agents_hosting_router( 74 | self.router['id']) 75 | # Now remove router from all agents 76 | for agent in body['agents']: 77 | self.admin_client.remove_router_from_l3_agent( 78 | agent['id'], self.router['id']) 79 | 80 | # Now list agents which host router again - list should be empty 81 | body = self.admin_client.list_l3_agents_hosting_router( 82 | self.router['id']) 83 | self.assertEqual([], body['agents']) 84 | 85 | # Now add router to one of agents 86 | self.admin_client.add_router_to_l3_agent( 87 | self.agent['id'], 88 | router_id=self.router['id']) 89 | 90 | # And check that router is hosted by this agent 91 | body = self.admin_client.list_l3_agents_hosting_router( 92 | self.router['id']) 93 | for agent in body['agents']: 94 | l3_agent_ids.append(agent['id']) 95 | self.assertIn('agent_type', agent) 96 | self.assertEqual('L3 agent', agent['agent_type']) 97 | self.assertIn(self.agent['id'], l3_agent_ids) 98 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/sfc_client.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Futurewei. All rights reserved. 2 | # Copyright 2017 Intel Corporation. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest import config 17 | 18 | from neutron_tempest_plugin.sfc.services import sfc_client 19 | 20 | CONF = config.CONF 21 | 22 | 23 | class SfcClientMixin(object): 24 | 25 | @classmethod 26 | def resource_setup(cls): 27 | super(SfcClientMixin, cls).resource_setup() 28 | manager = cls.os_admin 29 | cls.portchain_client = ( 30 | sfc_client.PortChainClient( 31 | manager.auth_provider, 32 | CONF.network.catalog_type, 33 | CONF.network.region or CONF.identity.region, 34 | endpoint_type=CONF.network.endpoint_type, 35 | build_interval=CONF.network.build_interval, 36 | build_timeout=CONF.network.build_timeout, 37 | **manager.default_params 38 | ) 39 | ) 40 | cls.portpairgroup_client = ( 41 | sfc_client.PortPairGroupClient( 42 | manager.auth_provider, 43 | CONF.network.catalog_type, 44 | CONF.network.region or CONF.identity.region, 45 | endpoint_type=CONF.network.endpoint_type, 46 | build_interval=CONF.network.build_interval, 47 | build_timeout=CONF.network.build_timeout, 48 | **manager.default_params 49 | ) 50 | ) 51 | cls.portpair_client = ( 52 | sfc_client.PortPairClient( 53 | manager.auth_provider, 54 | CONF.network.catalog_type, 55 | CONF.network.region or CONF.identity.region, 56 | endpoint_type=CONF.network.endpoint_type, 57 | build_interval=CONF.network.build_interval, 58 | build_timeout=CONF.network.build_timeout, 59 | **manager.default_params 60 | ) 61 | ) 62 | cls.sfcgraph_client = ( 63 | sfc_client.ServiceGraphClient( 64 | manager.auth_provider, 65 | CONF.network.catalog_type, 66 | CONF.network.region or CONF.identity.region, 67 | endpoint_type=CONF.network.endpoint_type, 68 | build_interval=CONF.network.build_interval, 69 | build_timeout=CONF.network.build_timeout, 70 | **manager.default_params 71 | ) 72 | ) 73 | 74 | @classmethod 75 | def create_port_chain(cls, **kwargs): 76 | body = cls.portchain_client.create_port_chain( 77 | **kwargs) 78 | pc = body['port_chain'] 79 | return pc 80 | 81 | @classmethod 82 | def create_port_pair_group(cls, **kwargs): 83 | body = cls.portpairgroup_client.create_port_pair_group( 84 | **kwargs) 85 | pg = body['port_pair_group'] 86 | return pg 87 | 88 | @classmethod 89 | def create_port_pair(cls, **kwargs): 90 | body = cls.portpair_client.create_port_pair( 91 | **kwargs) 92 | pp = body['port_pair'] 93 | return pp 94 | 95 | @classmethod 96 | def create_service_graph(cls, **kwargs): 97 | body = cls.sfcgraph_client.create_service_graph( 98 | **kwargs) 99 | pc = body['service_graph'] 100 | return pc 101 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/test_fip64.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Midokura SARL 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import netaddr 17 | 18 | from tempest.common import utils 19 | from tempest.common import waiters 20 | from tempest.lib.common.utils import data_utils 21 | from tempest.lib import decorators 22 | 23 | from neutron_tempest_plugin import config 24 | from neutron_tempest_plugin.scenario import base 25 | from neutron_tempest_plugin.scenario import constants 26 | 27 | 28 | CONF = config.CONF 29 | 30 | 31 | class Fip64(base.BaseTempestTestCase): 32 | credentials = ['primary', 'admin'] 33 | 34 | _fip_ip_version = 6 35 | 36 | @classmethod 37 | @utils.requires_ext(extension="fip64", service="network") 38 | def resource_setup(cls): 39 | super(Fip64, cls).resource_setup() 40 | cls.network = cls.create_network() 41 | cls.subnet = cls.create_subnet(cls.network) 42 | router = cls.create_router_by_client() 43 | cls.create_router_interface(router['id'], cls.subnet['id']) 44 | cls.keypair = cls.create_keypair() 45 | 46 | cls.secgroup = cls.os_primary.network_client.create_security_group( 47 | name=data_utils.rand_name('secgroup-'))['security_group'] 48 | cls.security_groups.append(cls.secgroup) 49 | cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id']) 50 | 51 | def _find_ipv6_subnet(self, network_id): 52 | subnets = self.os_admin.network_client.list_subnets( 53 | network_id=network_id)['subnets'] 54 | for subnet in subnets: 55 | if subnet['ip_version'] == self._fip_ip_version: 56 | return subnet['id'] 57 | msg = "No suitable subnets on public network" 58 | raise self.skipException(msg) 59 | 60 | def _create_and_associate_floatingip64(self, port_id): 61 | network_id = CONF.network.public_network_id 62 | subnet_id = self._find_ipv6_subnet(network_id) 63 | fip = self.os_primary.network_client.create_floatingip( 64 | floating_network_id=network_id, 65 | subnet_id=subnet_id, 66 | port_id=port_id)['floatingip'] 67 | self.floating_ips.append(fip) 68 | self.assertEqual( 69 | self._fip_ip_version, 70 | netaddr.IPAddress(fip['floating_ip_address']).version) 71 | return fip 72 | 73 | def _create_server_with_fip64(self): 74 | port = self.create_port(self.network, security_groups=[ 75 | self.secgroup['id']]) 76 | fip = self._create_and_associate_floatingip64(port['id']) 77 | server = self.create_server( 78 | flavor_ref=CONF.compute.flavor_ref, 79 | image_ref=CONF.compute.image_ref, 80 | key_name=self.keypair['name'], 81 | networks=[{'port': port['id']}])['server'] 82 | waiters.wait_for_server_status(self.os_primary.servers_client, 83 | server['id'], 84 | constants.SERVER_STATUS_ACTIVE) 85 | return {'port': port, 'fip': fip, 'server': server} 86 | 87 | @decorators.idempotent_id('63f7da91-c7dd-449b-b50b-1c56853ce0ef') 88 | def test_fip64(self): 89 | server = self._create_server_with_fip64() 90 | self.check_connectivity(server['fip']['floating_ip_address'], 91 | CONF.validation.image_ssh_user, 92 | self.keypair['private_key']) 93 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_subnetpool_prefix_ops.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 SUSE LLC 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import netaddr 17 | from tempest.common import utils 18 | from tempest.lib import decorators 19 | 20 | from neutron_tempest_plugin.api import test_subnetpools 21 | 22 | SUBNETPOOL_NAME = 'smoke-subnetpool' 23 | SUBNET_NAME = 'smoke-subnet' 24 | 25 | 26 | class SubnetPoolPrefixOpsTestMixin(object): 27 | 28 | def _compare_prefix_lists(self, list_expected, list_observed): 29 | expected_set = netaddr.IPSet(iterable=list_expected) 30 | observed_set = netaddr.IPSet(iterable=list_observed) 31 | 32 | # compact the IPSet's 33 | expected_set.compact() 34 | observed_set.compact() 35 | 36 | self.assertEqual(expected_set, observed_set) 37 | 38 | @decorators.idempotent_id('b1d56d1f-2818-44ee-b6a3-3c1327c25318') 39 | @utils.requires_ext(extension='subnetpool-prefix-ops', service='network') 40 | def test_add_remove_prefix(self): 41 | created_subnetpool = self._create_subnetpool() 42 | req_body = {'prefixes': self.prefixes_to_add} 43 | 44 | # Add a prefix to the subnet pool 45 | resp = self.client.add_subnetpool_prefix(created_subnetpool['id'], 46 | **req_body) 47 | self._compare_prefix_lists(self.prefixes + self.prefixes_to_add, 48 | resp['prefixes']) 49 | 50 | # Remove the prefix from the subnet pool 51 | resp = self.client.remove_subnetpool_prefix(created_subnetpool['id'], 52 | **req_body) 53 | self._compare_prefix_lists(self.prefixes, resp['prefixes']) 54 | 55 | @decorators.idempotent_id('a36c18fc-10b5-4ebc-ab79-914f826c5bf5') 56 | @utils.requires_ext(extension='subnetpool-prefix-ops', service='network') 57 | def test_add_overlapping_prefix(self): 58 | created_subnetpool = self._create_subnetpool() 59 | req_body = {'prefixes': self.overlapping_prefixes} 60 | 61 | # Add an overlapping prefix to the subnet pool 62 | resp = self.client.add_subnetpool_prefix(created_subnetpool['id'], 63 | **req_body) 64 | self._compare_prefix_lists(self.prefixes + self.overlapping_prefixes, 65 | resp['prefixes']) 66 | 67 | 68 | class SubnetPoolPrefixOpsIpv4Test(test_subnetpools.SubnetPoolsTestBase, 69 | SubnetPoolPrefixOpsTestMixin): 70 | 71 | prefixes = ['192.168.1.0/24', '10.10.10.0/24'] 72 | prefixes_to_add = ['192.168.2.0/24'] 73 | overlapping_prefixes = ['10.10.0.0/16'] 74 | min_prefixlen = 16 75 | ip_version = 4 76 | 77 | @classmethod 78 | def resource_setup(cls): 79 | super(SubnetPoolPrefixOpsIpv4Test, cls).resource_setup() 80 | cls._subnetpool_data = {'prefixes': cls.prefixes, 81 | 'min_prefixlen': cls.min_prefixlen} 82 | 83 | 84 | class SubnetPoolPrefixOpsIpv6Test(test_subnetpools.SubnetPoolsTestBase, 85 | SubnetPoolPrefixOpsTestMixin): 86 | 87 | prefixes = ['2001:db8:1234::/48', '2001:db8:1235::/48'] 88 | prefixes_to_add = ['2001:db8:4321::/48'] 89 | overlapping_prefixes = ['2001:db8:1234:1111::/64'] 90 | min_prefixlen = 48 91 | ip_version = 6 92 | 93 | @classmethod 94 | def resource_setup(cls): 95 | super(SubnetPoolPrefixOpsIpv6Test, cls).resource_setup() 96 | cls._subnetpool_data = {'prefixes': cls.prefixes, 97 | 'min_prefixlen': cls.min_prefixlen} 98 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/sfc/tests/scenario/manager.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 OpenStack Foundation 2 | # Copyright 2013 IBM Corp. 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from oslo_log import log 18 | 19 | from tempest import config 20 | from tempest.lib.common.utils import data_utils 21 | from tempest.lib.common.utils import test_utils 22 | from tempest.lib import exceptions as lib_exc 23 | from tempest.scenario import manager 24 | 25 | CONF = config.CONF 26 | 27 | LOG = log.getLogger(__name__) 28 | 29 | 30 | class ScenarioTest(manager.NetworkScenarioTest): 31 | """Base class for scenario tests. Uses tempest own clients. """ 32 | 33 | credentials = ['primary'] 34 | 35 | # ## Test functions library 36 | # 37 | # The create_[resource] functions only return body and discard the 38 | # resp part which is not used in scenario tests 39 | 40 | def _log_net_info(self, exc): 41 | # network debug is called as part of ssh init 42 | if not isinstance(exc, lib_exc.SSHTimeout): 43 | LOG.debug('Network information on a devstack host') 44 | 45 | def check_public_network_connectivity(self, ip_address, username, 46 | private_key, should_connect=True, 47 | msg=None, servers=None, mtu=None): 48 | # The target login is assumed to have been configured for 49 | # key-based authentication by cloud-init. 50 | LOG.debug('checking network connections to IP %s with user: %s', 51 | ip_address, username) 52 | try: 53 | self.check_vm_connectivity(ip_address, 54 | username, 55 | private_key, 56 | should_connect=should_connect, 57 | mtu=mtu) 58 | except Exception: 59 | ex_msg = 'Public network connectivity check failed' 60 | if msg: 61 | ex_msg += ": " + msg 62 | LOG.exception(ex_msg) 63 | self.log_console_output(servers) 64 | raise 65 | 66 | 67 | class NetworkScenarioTest(ScenarioTest): 68 | """Base class for network scenario tests. 69 | 70 | This class provide helpers for network scenario tests, using the neutron 71 | API. Helpers from ancestor which use the nova network API are overridden 72 | with the neutron API. 73 | 74 | This Class also enforces using Neutron instead of novanetwork. 75 | Subclassed tests will be skipped if Neutron is not enabled 76 | 77 | """ 78 | 79 | credentials = ['primary', 'admin'] 80 | 81 | @classmethod 82 | def skip_checks(cls): 83 | super(NetworkScenarioTest, cls).skip_checks() 84 | if not CONF.service_available.neutron: 85 | raise cls.skipException('Neutron not available') 86 | 87 | def _create_router(self, client=None, tenant_id=None, 88 | namestart='router-smoke'): 89 | if not client: 90 | client = self.routers_client 91 | if not tenant_id: 92 | tenant_id = client.project_id 93 | name = data_utils.rand_name(namestart) 94 | result = client.create_router(name=name, 95 | admin_state_up=True, 96 | tenant_id=tenant_id) 97 | router = result['router'] 98 | self.assertEqual(router['name'], name) 99 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 100 | client.delete_router, 101 | router['id']) 102 | return router 103 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_logging.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Fujitsu Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib.common.utils import data_utils 16 | from tempest.lib import decorators 17 | from tempest.lib import exceptions 18 | 19 | from neutron_tempest_plugin.api import base 20 | 21 | 22 | class LoggingTestJSON(base.BaseAdminNetworkTest): 23 | 24 | required_extensions = ['logging', 'standard-attr-description'] 25 | 26 | @decorators.idempotent_id('8d2e1ba5-455b-4519-a88e-e587002faba6') 27 | def test_log_lifecycle(self): 28 | security_group = self.create_security_group() 29 | name = data_utils.rand_name('test-log') 30 | description = data_utils.rand_name('test-log-desc') 31 | log = self.create_log(name=name, description=description, 32 | resource_id=security_group['id'], 33 | resource_type='security_group', enabled=True) 34 | 35 | # Test 'show log' 36 | retrieved_log = self.admin_client.show_log(log['id'])['log'] 37 | self.assertEqual(name, retrieved_log['name']) 38 | self.assertEqual(description, retrieved_log['description']) 39 | self.assertEqual('security_group', retrieved_log['resource_type']) 40 | self.assertTrue(retrieved_log['enabled']) 41 | 42 | # Test 'list logs' 43 | logs = self.admin_client.list_logs()['logs'] 44 | logs_ids = [log_object['id'] for log_object in logs] 45 | self.assertIn(log['id'], logs_ids) 46 | 47 | # Test 'update log' 48 | update_description = data_utils.rand_name('test-log') 49 | self.admin_client.update_log(log['id'], 50 | description=update_description, 51 | enabled=False) 52 | retrieved_log = self.admin_client.show_log(log['id'])['log'] 53 | self.assertEqual(update_description, retrieved_log['description']) 54 | self.assertFalse(retrieved_log['enabled']) 55 | 56 | # Test 'delete log' 57 | self.admin_client.delete_log(log['id']) 58 | self.assertRaises(exceptions.NotFound, 59 | self.admin_client.show_log, log['id']) 60 | 61 | @decorators.idempotent_id('1af6cdab-0eb0-4e13-8027-d89cf1c7a87a') 62 | def test_list_supported_logging_types(self): 63 | # List supported logging types 64 | # Since returned logging types depends on loaded backend drivers 65 | # this test is checking only if returned keys are same as expected keys 66 | expected_log_keys = ['type'] 67 | 68 | log_types = self.admin_client.list_loggable_resources() 69 | actual_list_log_types = log_types['loggable_resources'] 70 | 71 | # Verify that only required fields present in logging types 72 | for log_type in actual_list_log_types: 73 | self.assertEqual(tuple(expected_log_keys), tuple(log_type.keys())) 74 | 75 | @decorators.idempotent_id('1ab4eb2a-76f5-45b9-816b-1aa497a71eea') 76 | def test_log_deleted_with_corresponding_security_group(self): 77 | security_group = self.create_security_group() 78 | name = data_utils.rand_name('test-log') 79 | log = self.create_log( 80 | name=name, 81 | resource_type='security_group', 82 | resource_id=security_group['id'], 83 | enabled=True) 84 | 85 | # Ensure log was created 86 | retrieved_log = self.admin_client.show_log(log['id'])['log'] 87 | self.assertEqual(name, retrieved_log['name']) 88 | self.assertEqual(security_group['id'], retrieved_log['resource_id']) 89 | self.assertEqual('security_group', retrieved_log['resource_type']) 90 | self.assertTrue(retrieved_log['enabled']) 91 | 92 | # Delete SG 93 | self.delete_security_group(security_group) 94 | 95 | # Ensure log is also deleted 96 | self.assertRaises(exceptions.NotFound, 97 | self.admin_client.show_log, log['id']) 98 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/test_local_ip.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Huawei, Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | # 15 | 16 | from oslo_log import log as logging 17 | from tempest.common import utils 18 | from tempest.common import waiters 19 | from tempest.lib.common.utils import data_utils 20 | from tempest.lib import decorators 21 | 22 | from neutron_tempest_plugin.common import ssh 23 | from neutron_tempest_plugin import config 24 | from neutron_tempest_plugin.scenario import base 25 | from neutron_tempest_plugin.scenario import constants as const 26 | 27 | LOG = logging.getLogger(__name__) 28 | CONF = config.CONF 29 | 30 | 31 | class LocalIPTest(base.BaseTempestTestCase): 32 | credentials = ['primary', 'admin'] 33 | 34 | @classmethod 35 | @utils.requires_ext(extension="local_ip", service="network") 36 | def resource_setup(cls): 37 | super(LocalIPTest, cls).resource_setup() 38 | cls.network = cls.create_network() 39 | cls.subnet = cls.create_subnet(cls.network) 40 | cls.keypair = cls.create_keypair() 41 | 42 | # Create security group with admin privileges 43 | cls.secgroup = cls.create_security_group( 44 | name=data_utils.rand_name('secgroup')) 45 | 46 | # Execute funcs to achieve ssh and ICMP capabilities 47 | cls.create_loginable_secgroup_rule(secgroup_id=cls.secgroup['id']) 48 | cls.create_pingable_secgroup_rule(secgroup_id=cls.secgroup['id']) 49 | 50 | # Create router 51 | cls.router = cls.create_router( 52 | router_name=data_utils.rand_name("router-test"), 53 | admin_state_up=True, 54 | external_network_id=CONF.network.public_network_id) 55 | cls.create_router_interface(cls.router['id'], cls.subnet['id']) 56 | 57 | def _create_server(self, name=None): 58 | port = self.create_port( 59 | self.network, security_groups=[self.secgroup['id']]) 60 | server = self.create_server( 61 | flavor_ref=CONF.compute.flavor_ref, 62 | image_ref=CONF.compute.image_ref, 63 | key_name=self.keypair['name'], name=name, 64 | networks=[{'port': port['id']}])['server'] 65 | waiters.wait_for_server_status(self.os_primary.servers_client, 66 | server['id'], 67 | const.SERVER_STATUS_ACTIVE) 68 | 69 | return {'port': port, 'server': server} 70 | 71 | @decorators.idempotent_id('3aa4b288-011a-4aa2-9024-19ad2ce40bfd') 72 | def test_local_ip_connectivity(self): 73 | server1 = self._create_server(name='local_ip_vm1') 74 | server2 = self._create_server(name='local_ip_vm2') 75 | 76 | fip = self.create_and_associate_floatingip(server1['port']['id']) 77 | ssh_client = ssh.Client( 78 | fip['floating_ip_address'], 79 | CONF.validation.image_ssh_user, 80 | pkey=self.keypair['private_key']) 81 | 82 | servers = [server1['server'], server2['server']] 83 | 84 | # first check basic connectivity 85 | self.check_remote_connectivity( 86 | ssh_client, 87 | server2['port']['fixed_ips'][0]['ip_address'], 88 | servers=servers) 89 | 90 | local_ip = self.create_local_ip(network_id=self.network['id']) 91 | self.create_local_ip_association(local_ip['id'], 92 | fixed_port_id=server2['port']['id']) 93 | # check connectivity with local ip address 94 | self.check_remote_connectivity( 95 | ssh_client, local_ip['local_ip_address'], 96 | servers=servers, check_response_ip=False) 97 | 98 | # check basic connectivity after local ip association 99 | self.check_remote_connectivity( 100 | ssh_client, 101 | server2['port']['fixed_ips'][0]['ip_address'], 102 | servers=servers, 103 | check_response_ip=False) 104 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_address_scopes_negative.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from tempest.lib.common.utils import data_utils 16 | from tempest.lib import decorators 17 | from tempest.lib import exceptions as lib_exc 18 | 19 | from neutron_tempest_plugin.api import test_address_scopes 20 | 21 | 22 | class AddressScopeTestNegative(test_address_scopes.AddressScopeTestBase): 23 | 24 | @decorators.attr(type='negative') 25 | @decorators.idempotent_id('9c92ec34-0c50-4104-aa47-9ce98d5088df') 26 | def test_tenant_create_shared_address_scope(self): 27 | self.assertRaises(lib_exc.Forbidden, self._create_address_scope, 28 | shared=True, ip_version=4) 29 | 30 | @decorators.attr(type='negative') 31 | @decorators.idempotent_id('a857b61e-bf53-4fab-b21a-b0daaf81b5bd') 32 | def test_tenant_update_address_scope_shared_true(self): 33 | self.assertRaises(lib_exc.Forbidden, 34 | self._test_update_address_scope_helper, shared=True) 35 | 36 | @decorators.attr(type='negative') 37 | @decorators.idempotent_id('a859ef2f-9c76-4e2e-ba0f-e0339a489e8c') 38 | def test_tenant_update_address_scope_shared_false(self): 39 | self.assertRaises(lib_exc.Forbidden, 40 | self._test_update_address_scope_helper, shared=False) 41 | 42 | @decorators.attr(type='negative') 43 | @decorators.idempotent_id('9b6dd7ad-cabb-4f55-bd5e-e61176ef41f6') 44 | def test_get_non_existent_address_scope(self): 45 | non_exist_id = data_utils.rand_name('address_scope') 46 | self.assertRaises(lib_exc.NotFound, self.client.show_address_scope, 47 | non_exist_id) 48 | 49 | @decorators.attr(type='negative') 50 | @decorators.idempotent_id('ef213552-f2da-487d-bf4a-e1705d115ff1') 51 | def test_tenant_get_not_shared_admin_address_scope(self): 52 | address_scope = self._create_address_scope(is_admin=True, 53 | ip_version=4) 54 | # None-shared admin address scope cannot be retrieved by tenant user. 55 | self.assertRaises(lib_exc.NotFound, self.client.show_address_scope, 56 | address_scope['id']) 57 | 58 | @decorators.attr(type='negative') 59 | @decorators.idempotent_id('5c25dc6a-1e92-467a-9cc7-cda74b6003db') 60 | def test_delete_non_existent_address_scope(self): 61 | non_exist_id = data_utils.rand_name('address_scope') 62 | self.assertRaises(lib_exc.NotFound, self.client.delete_address_scope, 63 | non_exist_id) 64 | 65 | @decorators.attr(type='negative') 66 | @decorators.idempotent_id('47c25dc5-e886-4a84-88c3-ac5031969661') 67 | def test_update_non_existent_address_scope(self): 68 | non_exist_id = data_utils.rand_name('address_scope') 69 | self.assertRaises(lib_exc.NotFound, self.client.update_address_scope, 70 | non_exist_id, name='foo-name') 71 | 72 | @decorators.attr(type='negative') 73 | @decorators.idempotent_id('702d0515-82cb-4207-b0d9-703336e54665') 74 | def test_update_shared_address_scope_to_unshare(self): 75 | address_scope = self._create_address_scope(is_admin=True, shared=True, 76 | ip_version=4) 77 | self.assertRaises(lib_exc.BadRequest, 78 | self.admin_client.update_address_scope, 79 | address_scope['id'], name='new-name', shared=False) 80 | 81 | @decorators.attr(type='negative') 82 | @decorators.idempotent_id('1e471e5c-6f9c-437a-9257-fd9bc4b6f0fb') 83 | def test_delete_address_scope_associated_with_subnetpool(self): 84 | address_scope = self._create_address_scope(ip_version=4) 85 | prefixes = [u'10.11.12.0/24'] 86 | subnetpool_data = { 87 | 'name': 'foo-subnetpool', 88 | 'min_prefixlen': '29', 'prefixes': prefixes, 89 | 'address_scope_id': address_scope['id']} 90 | self.create_subnetpool(**subnetpool_data) 91 | self.assertRaises(lib_exc.Conflict, self.client.delete_address_scope, 92 | address_scope['id']) 93 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/neutron_dynamic_routing/scenario/basic/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 VA Linux Systems Japan K.K. 2 | # Copyright (C) 2016 Fumihiko Kakuma 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from os_ken.tests.integrated.common import docker_base as ctn_base 18 | from os_ken.tests.integrated.common import quagga 19 | 20 | from neutron_tempest_plugin.neutron_dynamic_routing.scenario import base 21 | 22 | 23 | class BgpSpeakerBasicTestJSONBase(base.BgpSpeakerScenarioTestJSONBase): 24 | 25 | RAS_MAX = 3 26 | public_gw = '192.168.20.1' 27 | MyScope = base.Scope(name='my-scope') 28 | PNet = base.Net(name='', net='172.24.6.0', mask=24, 29 | cidr='172.24.6.0/24', router=None) 30 | PPool = base.Pool(name='test-pool-ext', prefixlen=PNet.mask, 31 | prefixes=[PNet.net + '/8']) 32 | PSubNet = base.SubNet(name='', cidr=PNet.cidr, mask=PNet.mask) 33 | TPool = base.Pool(name='tenant-test-pool', prefixlen=28, 34 | prefixes=['10.10.0.0/16']) 35 | L_AS = base.AS(asn='64512', router_id='192.168.0.2', adv_net='') 36 | ras_l = [ 37 | base.AS(asn='64522', router_id='192.168.0.12', 38 | adv_net='192.168.162.0/24'), 39 | base.AS(asn='64523', router_id='192.168.0.13', 40 | adv_net='192.168.163.0/24'), 41 | base.AS(asn='64524', router_id='192.168.0.14', 42 | adv_net='192.168.164.0/24') 43 | ] 44 | 45 | bgp_speaker_args = { 46 | 'local_as': L_AS.asn, 47 | 'ip_version': 4, 48 | 'name': 'my-bgp-speaker1', 49 | 'advertise_floating_ip_host_routes': True, 50 | 'advertise_tenant_networks': True 51 | } 52 | bgp_peer_args = [ 53 | {'remote_as': ras_l[0].asn, 54 | 'name': 'my-bgp-peer1', 55 | 'peer_ip': None, 56 | 'auth_type': 'none'}, 57 | {'remote_as': ras_l[1].asn, 58 | 'name': 'my-bgp-peer2', 59 | 'peer_ip': None, 60 | 'auth_type': 'none'}, 61 | {'remote_as': ras_l[2].asn, 62 | 'name': 'my-bgp-peer3', 63 | 'peer_ip': None, 64 | 'auth_type': 'none'} 65 | ] 66 | 67 | def setUp(self): 68 | super(BgpSpeakerBasicTestJSONBase, self).setUp() 69 | 70 | @classmethod 71 | def resource_setup_container(cls): 72 | cls.brdc = ctn_base.Bridge(name='br-docker-basic', 73 | subnet='192.168.20.0/24', 74 | start_ip='192.168.20.128', 75 | end_ip='192.168.20.254', 76 | self_ip=True, 77 | fixed_ip=cls.public_gw + '/24', 78 | br_type=base.BRIDGE_TYPE) 79 | cls.bridges.append(cls.brdc) 80 | # This is dummy container object for a dr service. 81 | # This keeps data which passes to a quagga container. 82 | cls.dr = ctn_base.BGPContainer(name='dummy-dr-basic', 83 | asn=int(cls.L_AS.asn), 84 | router_id=cls.L_AS.router_id) 85 | cls.dr.set_addr_info(bridge='br-docker-basic', ipv4=cls.public_gw) 86 | # quagga container 87 | cls.dockerimg = ctn_base.DockerImage(baseimage=cls.baseimage) 88 | cls.q_img = cls.dockerimg.create_quagga(check_exist=True) 89 | cls.images.append(cls.q_img) 90 | for i in range(cls.RAS_MAX): 91 | qg = quagga.QuaggaBGPContainer(name='q-basic-' + str(i + 1), 92 | asn=int(cls.ras_l[i].asn), 93 | router_id=cls.ras_l[i].router_id, 94 | ctn_image_name=cls.q_img) 95 | cls.containers.append(qg) 96 | cls.r_ass.append(qg) 97 | qg.add_route(cls.ras_l[i].adv_net) 98 | qg.run(wait=True) 99 | cls.r_as_ip.append(cls.brdc.addif(qg)) 100 | qg.add_peer(cls.dr, bridge=cls.brdc.name, 101 | peer_info={'passive': True}) 102 | 103 | cls.tnet_gen = cls.get_subnet(start='10.10.1.0', 104 | end='10.10.255.0', 105 | step=256) 106 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_agent_management.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 IBM Corp. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from neutron_tempest_plugin.common import tempest_fixtures 16 | from tempest.lib.common.utils import data_utils 17 | from tempest.lib import decorators 18 | from tempest.lib import exceptions as lib_exc 19 | 20 | from neutron_tempest_plugin.api import base 21 | 22 | 23 | class AgentManagementTestJSON(base.BaseAdminNetworkTest): 24 | 25 | required_extensions = ['agent'] 26 | 27 | @classmethod 28 | def resource_setup(cls): 29 | super(AgentManagementTestJSON, cls).resource_setup() 30 | body = cls.admin_client.list_agents() 31 | agents = body['agents'] 32 | cls.agent = agents[0] # don't modify this agent 33 | 34 | @decorators.idempotent_id('9c80f04d-11f3-44a4-8738-ed2f879b0ff4') 35 | def test_list_agent(self): 36 | body = self.admin_client.list_agents() 37 | agents = body['agents'] 38 | # Heartbeats must be excluded from comparison 39 | self.agent.pop('heartbeat_timestamp', None) 40 | self.agent.pop('configurations', None) 41 | # Exclude alive as it can happen that when testclass' 42 | # resource_setup executed the selected agent is not up 43 | self.agent.pop('alive', None) 44 | for agent in agents: 45 | agent.pop('heartbeat_timestamp', None) 46 | agent.pop('configurations', None) 47 | agent.pop('alive', None) 48 | self.assertIn(self.agent, agents) 49 | 50 | @decorators.idempotent_id('e335be47-b9a1-46fd-be30-0874c0b751e6') 51 | def test_list_agents_non_admin(self): 52 | body = self.client.list_agents() 53 | self.assertEqual(len(body["agents"]), 0) 54 | 55 | @decorators.idempotent_id('869bc8e8-0fda-4a30-9b71-f8a7cf58ca9f') 56 | def test_show_agent(self): 57 | body = self.admin_client.show_agent(self.agent['id']) 58 | agent = body['agent'] 59 | self.assertEqual(agent['id'], self.agent['id']) 60 | 61 | @decorators.idempotent_id('371dfc5b-55b9-4cb5-ac82-c40eadaac941') 62 | def test_update_agent_status(self): 63 | origin_status = self.agent['admin_state_up'] 64 | # Try to update the 'admin_state_up' to the original 65 | # one to avoid the negative effect. 66 | agent_status = {'admin_state_up': origin_status} 67 | body = self.admin_client.update_agent(agent_id=self.agent['id'], 68 | agent_info=agent_status) 69 | updated_status = body['agent']['admin_state_up'] 70 | self.assertEqual(origin_status, updated_status) 71 | 72 | @decorators.idempotent_id('68a94a14-1243-46e6-83bf-157627e31556') 73 | def test_update_agent_description(self): 74 | agents = self.admin_client.list_agents()['agents'] 75 | dyn_agent = self._select_one_agent_for_update(agents) 76 | 77 | self.useFixture(tempest_fixtures.LockFixture('agent_description')) 78 | description = 'description for update agent.' 79 | agent_description = {'description': description} 80 | body = self.admin_client.update_agent(agent_id=dyn_agent['id'], 81 | agent_info=agent_description) 82 | self.addCleanup(self._restore_agent, dyn_agent) 83 | updated_description = body['agent']['description'] 84 | self.assertEqual(updated_description, description) 85 | 86 | def _restore_agent(self, dyn_agent): 87 | """Restore the agent description after update test.""" 88 | description = dyn_agent['description'] 89 | origin_agent = {'description': description} 90 | self.admin_client.update_agent(agent_id=dyn_agent['id'], 91 | agent_info=origin_agent) 92 | 93 | def _select_one_agent_for_update(self, agents): 94 | """Return one agent that is not the one selected at resource_setup""" 95 | for agent in agents: 96 | if self.agent['id'] != agent['id']: 97 | return agent 98 | raise self.skipException("This test requires at least two agents.") 99 | 100 | @decorators.idempotent_id('b33af888-b6ac-4e68-a0ca-0444c2696cf9') 101 | def test_delete_agent_negative(self): 102 | non_existent_id = data_utils.rand_uuid() 103 | self.assertRaises( 104 | lib_exc.NotFound, 105 | self.admin_client.delete_agent, non_existent_id) 106 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/tap_as_a_service/base.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import tempest.api.network.base as test 14 | from tempest import config 15 | from tempest.lib.common.utils import data_utils 16 | from tempest.lib.common.utils import test_utils 17 | 18 | from neutron_tempest_plugin.tap_as_a_service.services import taas_client 19 | 20 | CONF = config.CONF 21 | 22 | 23 | class BaseTaasTest(test.BaseAdminNetworkTest): 24 | 25 | @classmethod 26 | def resource_setup(cls): 27 | super(BaseTaasTest, cls).resource_setup() 28 | os_primary = cls.os_primary 29 | cls.tap_services_client = taas_client.TapServicesClient( 30 | os_primary.auth_provider, 31 | CONF.network.catalog_type, 32 | CONF.network.region or CONF.identity.region, 33 | endpoint_type=CONF.network.endpoint_type, 34 | build_interval=CONF.network.build_interval, 35 | build_timeout=CONF.network.build_timeout, 36 | **os_primary.default_params) 37 | cls.tap_flows_client = taas_client.TapFlowsClient( 38 | os_primary.auth_provider, 39 | CONF.network.catalog_type, 40 | CONF.network.region or CONF.identity.region, 41 | endpoint_type=CONF.network.endpoint_type, 42 | build_interval=CONF.network.build_interval, 43 | build_timeout=CONF.network.build_timeout, 44 | **os_primary.default_params) 45 | cls.tap_mirrors_client = taas_client.TapMirrorsClient( 46 | os_primary.auth_provider, 47 | CONF.network.catalog_type, 48 | CONF.network.region or CONF.identity.region, 49 | endpoint_type=CONF.network.endpoint_type, 50 | build_interval=CONF.network.build_interval, 51 | build_timeout=CONF.network.build_timeout, 52 | **os_primary.default_params) 53 | 54 | def create_tap_service(self, **kwargs): 55 | body = self.tap_services_client.create_tap_service( 56 | name=data_utils.rand_name("tap_service"), 57 | **kwargs) 58 | tap_service = body['tap_service'] 59 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 60 | self.tap_services_client.delete_tap_service, 61 | tap_service['id']) 62 | return tap_service 63 | 64 | def create_tap_flow(self, **kwargs): 65 | body = self.tap_flows_client.create_tap_flow( 66 | name=data_utils.rand_name("tap_service"), 67 | **kwargs) 68 | tap_flow = body['tap_flow'] 69 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 70 | self.tap_flows_client.delete_tap_flow, 71 | tap_flow['id']) 72 | return tap_flow 73 | 74 | def update_tap_service(self, tap_service_id, **kwargs): 75 | body = self.tap_services_client.update_tap_service( 76 | tap_service_id, 77 | **kwargs) 78 | tap_service = body['tap_service'] 79 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 80 | self.tap_services_client.delete_tap_service, 81 | tap_service['id']) 82 | 83 | def update_tap_flow(self, tap_flow_id, **kwargs): 84 | body = self.tap_flows_client.update_tap_flow( 85 | tap_flow_id, 86 | **kwargs) 87 | tap_flow = body['tap_flow'] 88 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 89 | self.tap_flows_client.delete_tap_flow, 90 | tap_flow['id']) 91 | 92 | def create_tap_mirror(self, **kwargs): 93 | body = self.tap_mirrors_client.create_tap_mirror( 94 | name=data_utils.rand_name("tap_mirror"), 95 | **kwargs) 96 | tap_mirror = body['tap_mirror'] 97 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 98 | self.tap_mirrors_client.delete_tap_mirror, 99 | tap_mirror['id']) 100 | return tap_mirror 101 | 102 | def update_tap_mirror(self, tap_mirror_id, **kwargs): 103 | body = self.tap_mirrors_client.update_tap_mirror( 104 | tap_mirror_id, 105 | **kwargs) 106 | tap_mirror = body['tap_mirror'] 107 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, 108 | self.tap_mirrors_client.delete_tap_mirror, 109 | tap_mirror['id']) 110 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_subnets.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import netaddr 14 | from tempest.lib import decorators 15 | 16 | from neutron_tempest_plugin.api import base 17 | 18 | 19 | class SubnetsSearchCriteriaTest(base.BaseSearchCriteriaTest): 20 | 21 | resource = 'subnet' 22 | 23 | list_kwargs = {'shared': False} 24 | 25 | @classmethod 26 | def resource_setup(cls): 27 | if 'subnet-external-network' in cls.get_loaded_network_extensions(): 28 | cls.list_kwargs['router:external'] = False 29 | super(SubnetsSearchCriteriaTest, cls).resource_setup() 30 | net = cls.create_network(network_name='subnet-search-test-net') 31 | for name in cls.resource_names: 32 | cls.create_subnet(net, name=name) 33 | 34 | @decorators.idempotent_id('d2d61995-5dd5-4b93-bce7-3edefdb79563') 35 | def test_list_sorts_asc(self): 36 | self._test_list_sorts_asc() 37 | 38 | @decorators.idempotent_id('c3c6b0af-c4ac-4da0-b568-8d08ae550604') 39 | def test_list_sorts_desc(self): 40 | self._test_list_sorts_desc() 41 | 42 | @decorators.idempotent_id('b93063b3-f713-406e-bf93-e5738e09153c') 43 | def test_list_pagination(self): 44 | self._test_list_pagination() 45 | 46 | @decorators.idempotent_id('2ddd9aa6-de28-410f-9cbc-ce752893c407') 47 | def test_list_pagination_with_marker(self): 48 | self._test_list_pagination_with_marker() 49 | 50 | @decorators.idempotent_id('351183ef-6ed9-4d71-a9f2-a5ac049bd7ea') 51 | def test_list_pagination_with_href_links(self): 52 | self._test_list_pagination_with_href_links() 53 | 54 | @decorators.idempotent_id('dfaa20ca-6d84-4f26-962f-2fee4d247cd9') 55 | def test_list_pagination_page_reverse_asc(self): 56 | self._test_list_pagination_page_reverse_asc() 57 | 58 | @decorators.idempotent_id('40552213-3e12-4d6a-86f3-dda92f3de88c') 59 | def test_list_pagination_page_reverse_desc(self): 60 | self._test_list_pagination_page_reverse_desc() 61 | 62 | @decorators.idempotent_id('3cea9053-a731-4480-93ee-19b2c28a9ce4') 63 | def test_list_pagination_page_reverse_with_href_links(self): 64 | self._test_list_pagination_page_reverse_with_href_links() 65 | 66 | @decorators.idempotent_id('d851937c-9821-4b46-9d18-43e9077ecac0') 67 | def test_list_no_pagination_limit_0(self): 68 | self._test_list_no_pagination_limit_0() 69 | 70 | @decorators.idempotent_id('c0f9280b-9d81-4728-a967-6be22659d4c8') 71 | def test_list_validation_filters(self): 72 | self._test_list_validation_filters(self.list_kwargs) 73 | self._test_list_validation_filters({ 74 | 'unknown_filter': 'value'}, filter_is_valid=False) 75 | 76 | 77 | class SubnetServiceTypeTestJSON(base.BaseNetworkTest): 78 | 79 | required_extensions = ['subnet-service-types'] 80 | 81 | @classmethod 82 | def resource_setup(cls): 83 | super(SubnetServiceTypeTestJSON, cls).resource_setup() 84 | cls.network = cls.create_network() 85 | 86 | @decorators.idempotent_id('7e0edb66-1bb2-4473-ab83-d039cddced0d') 87 | def test_allocate_ips_are_from_correct_subnet(self): 88 | cidr_1 = netaddr.IPNetwork('192.168.1.0/24') 89 | cidr_2 = netaddr.IPNetwork('192.168.2.0/24') 90 | 91 | # NOTE(slaweq): service_type "network:distributed" is needed for 92 | # ML2/OVN backend. It's needed because OVN driver creates additional 93 | # port for metadata service in each subnet with enabled dhcp and such 94 | # port needs to have allocated IP address from the subnet also. 95 | self.create_subnet( 96 | self.network, 97 | service_types=['test:type_1', 'network:distributed'], 98 | cidr=str(cidr_1)) 99 | self.create_subnet( 100 | self.network, 101 | service_types=['test:type_2', 'network:distributed'], 102 | cidr=str(cidr_2)) 103 | port_type_1 = self.create_port(self.network, 104 | device_owner="test:type_1") 105 | port_type_2 = self.create_port(self.network, 106 | device_owner="test:type_2") 107 | 108 | self.assertEqual(1, len(port_type_1['fixed_ips'])) 109 | self.assertEqual(1, len(port_type_2['fixed_ips'])) 110 | self.assertIn( 111 | netaddr.IPAddress(port_type_1['fixed_ips'][0]['ip_address']), 112 | cidr_1) 113 | self.assertIn( 114 | netaddr.IPAddress(port_type_2['fixed_ips'][0]['ip_address']), 115 | cidr_2) 116 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/fwaas/services/v2_client.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib import exceptions as lib_exc 17 | from tempest.lib.services.network import base 18 | 19 | 20 | class FirewallGroupsClient(base.BaseNetworkClient): 21 | 22 | def create_firewall_group(self, **kwargs): 23 | uri = '/fwaas/firewall_groups' 24 | post_data = {'firewall_group': kwargs} 25 | return self.create_resource(uri, post_data) 26 | 27 | def update_firewall_group(self, firewall_group_id, **kwargs): 28 | uri = '/fwaas/firewall_groups/%s' % firewall_group_id 29 | post_data = {'firewall_group': kwargs} 30 | return self.update_resource(uri, post_data) 31 | 32 | def show_firewall_group(self, firewall_group_id, **fields): 33 | uri = '/fwaas/firewall_groups/%s' % firewall_group_id 34 | return self.show_resource(uri, **fields) 35 | 36 | def delete_firewall_group(self, firewall_group_id): 37 | uri = '/fwaas/firewall_groups/%s' % firewall_group_id 38 | return self.delete_resource(uri) 39 | 40 | def list_firewall_groups(self, **filters): 41 | uri = '/fwaas/firewall_groups' 42 | return self.list_resources(uri, **filters) 43 | 44 | def is_resource_deleted(self, id): 45 | try: 46 | self.show_firewall_group(id) 47 | except lib_exc.NotFound: 48 | return True 49 | return False 50 | 51 | @property 52 | def resource_type(self): 53 | """Returns the primary type of resource this client works with.""" 54 | return 'firewall_group' 55 | 56 | 57 | class FirewallRulesClient(base.BaseNetworkClient): 58 | 59 | def create_firewall_rule(self, **kwargs): 60 | uri = '/fwaas/firewall_rules' 61 | post_data = {'firewall_rule': kwargs} 62 | return self.create_resource(uri, post_data) 63 | 64 | def update_firewall_rule(self, firewall_rule_id, **kwargs): 65 | uri = '/fwaas/firewall_rules/%s' % firewall_rule_id 66 | post_data = {'firewall_rule': kwargs} 67 | return self.update_resource(uri, post_data) 68 | 69 | def show_firewall_rule(self, firewall_rule_id, **fields): 70 | uri = '/fwaas/firewall_rules/%s' % firewall_rule_id 71 | return self.show_resource(uri, **fields) 72 | 73 | def delete_firewall_rule(self, firewall_rule_id): 74 | uri = '/fwaas/firewall_rules/%s' % firewall_rule_id 75 | return self.delete_resource(uri) 76 | 77 | def list_firewall_rules(self, **filters): 78 | uri = '/fwaas/firewall_rules' 79 | return self.list_resources(uri, **filters) 80 | 81 | 82 | class FirewallPoliciesClient(base.BaseNetworkClient): 83 | 84 | def create_firewall_policy(self, **kwargs): 85 | uri = '/fwaas/firewall_policies' 86 | post_data = {'firewall_policy': kwargs} 87 | return self.create_resource(uri, post_data) 88 | 89 | def update_firewall_policy(self, firewall_policy_id, **kwargs): 90 | uri = '/fwaas/firewall_policies/%s' % firewall_policy_id 91 | post_data = {'firewall_policy': kwargs} 92 | return self.update_resource(uri, post_data) 93 | 94 | def show_firewall_policy(self, firewall_policy_id, **fields): 95 | uri = '/fwaas/firewall_policies/%s' % firewall_policy_id 96 | return self.show_resource(uri, **fields) 97 | 98 | def delete_firewall_policy(self, firewall_policy_id): 99 | uri = '/fwaas/firewall_policies/%s' % firewall_policy_id 100 | return self.delete_resource(uri) 101 | 102 | def list_firewall_policies(self, **filters): 103 | uri = '/fwaas/firewall_policies' 104 | return self.list_resources(uri, **filters) 105 | 106 | def insert_firewall_rule_in_policy(self, firewall_policy_id, 107 | firewall_rule_id, insert_after='', 108 | insert_before=''): 109 | uri = '/fwaas/firewall_policies/%s/insert_rule' % firewall_policy_id 110 | data = { 111 | 'firewall_rule_id': firewall_rule_id, 112 | 'insert_after': insert_after, 113 | 'insert_before': insert_before, 114 | } 115 | return self.update_resource(uri, data) 116 | 117 | def remove_firewall_rule_from_policy(self, firewall_policy_id, 118 | firewall_rule_id): 119 | uri = '/fwaas/firewall_policies/%s/remove_rule' % firewall_policy_id 120 | data = { 121 | 'firewall_rule_id': firewall_rule_id, 122 | } 123 | return self.update_resource(uri, data) 124 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/bgpvpn/services/bgpvpn_client.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015 Ericsson. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib.services.network import base 17 | 18 | # This is the representation of the bgpvpn 19 | # client of networking-bgpvpn 20 | 21 | BGPVPN_OBJECT_PATH = '/bgpvpn/bgpvpns' 22 | BGPVPN_RESOURCE_PATH = '/bgpvpn/bgpvpns/%s' 23 | BGPVPN_NETWORK_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/network_associations' 24 | BGPVPN_ROUTER_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/router_associations' 25 | BGPVPN_PORT_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/port_associations' 26 | 27 | 28 | class BgpvpnClient(base.BaseNetworkClient): 29 | 30 | def create_bgpvpn(self, **kwargs): 31 | uri = BGPVPN_OBJECT_PATH 32 | post_data = {'bgpvpn': kwargs} 33 | return self.create_resource(uri, post_data) 34 | 35 | def delete_bgpvpn(self, bgpvpn_id): 36 | uri = BGPVPN_RESOURCE_PATH % bgpvpn_id 37 | return self.delete_resource(uri) 38 | 39 | def show_bgpvpn(self, bgpvpn_id, **fields): 40 | uri = BGPVPN_RESOURCE_PATH % bgpvpn_id 41 | return self.show_resource(uri, **fields) 42 | 43 | def list_bgpvpns(self, **filters): 44 | uri = BGPVPN_OBJECT_PATH 45 | return self.list_resources(uri, **filters) 46 | 47 | def update_bgpvpn(self, bgpvpn_id, **kwargs): 48 | uri = BGPVPN_RESOURCE_PATH % bgpvpn_id 49 | post_data = {'bgpvpn': kwargs} 50 | return self.update_resource(uri, post_data) 51 | 52 | def create_network_association(self, bgpvpn_id, network_id): 53 | uri = BGPVPN_NETWORK_ASSOCIATION_PATH % bgpvpn_id 54 | post_data = {"network_association": 55 | {"network_id": network_id}} 56 | return self.create_resource(uri, post_data) 57 | 58 | def delete_network_association(self, bgpvpn_id, association_id): 59 | uri_pattern = BGPVPN_NETWORK_ASSOCIATION_PATH + "/%s" 60 | uri = uri_pattern % (bgpvpn_id, association_id) 61 | return self.delete_resource(uri) 62 | 63 | def show_network_association(self, bgpvpn_id, association_id, **fields): 64 | uri_pattern = BGPVPN_NETWORK_ASSOCIATION_PATH + "/%s" 65 | uri = uri_pattern % (bgpvpn_id, association_id) 66 | return self.show_resource(uri, **fields) 67 | 68 | def list_network_associations(self, bgpvpn_id, **filters): 69 | uri = BGPVPN_NETWORK_ASSOCIATION_PATH % bgpvpn_id 70 | return self.list_resources(uri, **filters) 71 | 72 | def create_router_association(self, bgpvpn_id, router_id): 73 | uri = BGPVPN_ROUTER_ASSOCIATION_PATH % bgpvpn_id 74 | post_data = {"router_association": 75 | {"router_id": router_id}} 76 | return self.create_resource(uri, post_data) 77 | 78 | def delete_router_association(self, bgpvpn_id, association_id): 79 | uri_pattern = BGPVPN_ROUTER_ASSOCIATION_PATH + "/%s" 80 | uri = uri_pattern % (bgpvpn_id, association_id) 81 | return self.delete_resource(uri) 82 | 83 | def show_router_association(self, bgpvpn_id, association_id, **fields): 84 | uri_pattern = BGPVPN_ROUTER_ASSOCIATION_PATH + "/%s" 85 | uri = uri_pattern % (bgpvpn_id, association_id) 86 | return self.show_resource(uri, **fields) 87 | 88 | def list_router_associations(self, bgpvpn_id, **filters): 89 | uri = BGPVPN_ROUTER_ASSOCIATION_PATH % bgpvpn_id 90 | return self.list_resources(uri, **filters) 91 | 92 | def create_port_association(self, bgpvpn_id, **kwargs): 93 | uri = BGPVPN_PORT_ASSOCIATION_PATH % bgpvpn_id 94 | post_data = {"port_association": kwargs} 95 | return self.create_resource(uri, post_data) 96 | 97 | def update_port_association(self, bgpvpn_id, association_id, **kwargs): 98 | uri_pattern = BGPVPN_PORT_ASSOCIATION_PATH + "/%s" 99 | uri = uri_pattern % (bgpvpn_id, association_id) 100 | post_data = {"port_association": kwargs} 101 | return self.update_resource(uri, post_data) 102 | 103 | def delete_port_association(self, bgpvpn_id, association_id): 104 | uri_pattern = BGPVPN_PORT_ASSOCIATION_PATH + "/%s" 105 | uri = uri_pattern % (bgpvpn_id, association_id) 106 | return self.delete_resource(uri) 107 | 108 | def show_port_association(self, bgpvpn_id, association_id, **fields): 109 | uri_pattern = BGPVPN_PORT_ASSOCIATION_PATH + "/%s" 110 | uri = uri_pattern % (bgpvpn_id, association_id) 111 | return self.show_resource(uri, **fields) 112 | 113 | def list_port_associations(self, bgpvpn_id, **filters): 114 | uri = BGPVPN_PORT_ASSOCIATION_PATH % bgpvpn_id 115 | return self.list_resources(uri, **filters) 116 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/admin/test_routers_flavors.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 3 | # not use this file except in compliance with the License. You may obtain 4 | # a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 10 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 11 | # License for the specific language governing permissions and limitations 12 | # under the License. 13 | 14 | from neutron_lib.plugins import constants 15 | from tempest.lib import decorators 16 | from tempest.lib import exceptions as lib_exc 17 | import testtools 18 | 19 | from neutron_tempest_plugin.api import base_routers as base 20 | 21 | 22 | class RoutersFlavorTestCase(base.BaseRouterTest): 23 | 24 | required_extensions = ['router', 'flavors', 'l3-flavors'] 25 | 26 | @classmethod 27 | def resource_setup(cls): 28 | super(RoutersFlavorTestCase, cls).resource_setup() 29 | cls.service_profiles = [] 30 | cls.flavor_service_profiles = [] 31 | # make a flavor based on legacy router for regular tenant to use 32 | driver = ('neutron.services.l3_router.service_providers.' 33 | 'single_node.SingleNodeDriver') 34 | try: 35 | sp = cls.admin_client.create_service_profile(driver=driver) 36 | except lib_exc.NotFound as e: 37 | if e.resp_body['type'] == 'ServiceProfileDriverNotFound': 38 | raise cls.skipException("%s is not available" % driver) 39 | raise 40 | cls.service_profiles.append(sp['service_profile']) 41 | cls.flavor = cls.create_flavor( 42 | name='special_flavor', 43 | description='econonomy class', 44 | service_type=constants.L3) 45 | cls.admin_client.create_flavor_service_profile( 46 | cls.flavor['id'], sp['service_profile']['id']) 47 | cls.flavor_service_profiles.append((cls.flavor['id'], 48 | sp['service_profile']['id'])) 49 | # make another with a different driver 50 | driver = ('neutron.services.l3_router.service_providers.' 51 | 'dvr.DvrDriver') 52 | try: 53 | sp = cls.admin_client.create_service_profile(driver=driver) 54 | except lib_exc.NotFound as e: 55 | if e.resp_body['type'] == 'ServiceProfileDriverNotFound': 56 | raise cls.skipException("%s is not available" % driver) 57 | raise 58 | cls.service_profiles.append(sp['service_profile']) 59 | cls.prem_flavor = cls.create_flavor( 60 | name='better_special_flavor', 61 | description='econonomy comfort', 62 | service_type=constants.L3) 63 | cls.admin_client.create_flavor_service_profile( 64 | cls.prem_flavor['id'], sp['service_profile']['id']) 65 | cls.flavor_service_profiles.append((cls.prem_flavor['id'], 66 | sp['service_profile']['id'])) 67 | 68 | @classmethod 69 | def resource_cleanup(cls): 70 | for flavor_id, service_profile_id in cls.flavor_service_profiles: 71 | cls.admin_client.delete_flavor_service_profile(flavor_id, 72 | service_profile_id) 73 | for service_profile in cls.service_profiles: 74 | cls.admin_client.delete_service_profile( 75 | service_profile['id']) 76 | super(RoutersFlavorTestCase, cls).resource_cleanup() 77 | 78 | @decorators.idempotent_id('a4d01977-e968-4983-b4d9-824ea6c33f4b') 79 | def test_create_router_with_flavor(self): 80 | # ensure regular client can see flavor 81 | flavors = self.client.list_flavors(id=self.flavor['id']) 82 | flavor = flavors['flavors'][0] 83 | self.assertEqual('special_flavor', flavor['name']) 84 | flavors = self.client.list_flavors(id=self.prem_flavor['id']) 85 | prem_flavor = flavors['flavors'][0] 86 | self.assertEqual('better_special_flavor', prem_flavor['name']) 87 | 88 | # ensure client can create router with both flavors 89 | router = self.create_router('name', flavor_id=flavor['id']) 90 | self.assertEqual(flavor['id'], router['flavor_id']) 91 | router = self.create_router('name', flavor_id=prem_flavor['id']) 92 | self.assertEqual(prem_flavor['id'], router['flavor_id']) 93 | 94 | @decorators.idempotent_id('30e73858-a0fc-409c-a2e0-e9cd2826f6a2') 95 | def test_delete_router_flavor_in_use(self): 96 | self.create_router('name', flavor_id=self.flavor['id']) 97 | with testtools.ExpectedException(lib_exc.Conflict): 98 | self.admin_client.delete_flavor(self.flavor['id']) 99 | 100 | @decorators.idempotent_id('83939cf7-5070-41bc-9a3e-cd9f22df2186') 101 | def test_badrequest_on_requesting_flags_and_flavor(self): 102 | with testtools.ExpectedException(lib_exc.BadRequest): 103 | self.admin_client.create_router( 104 | 'name', flavor_id=self.flavor['id'], distributed=True) 105 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/admin/test_floatingip.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from tempest.common import utils 16 | from tempest.common import waiters 17 | from tempest.lib import decorators 18 | 19 | from neutron_tempest_plugin.common import ssh 20 | from neutron_tempest_plugin import config 21 | from neutron_tempest_plugin.scenario import base 22 | from neutron_tempest_plugin.scenario import constants as const 23 | 24 | CONF = config.CONF 25 | 26 | 27 | class FloatingIpTestCasesAdmin(base.BaseTempestTestCase): 28 | credentials = ['primary', 'admin'] 29 | 30 | @classmethod 31 | def setup_clients(cls): 32 | super(FloatingIpTestCasesAdmin, cls).setup_clients() 33 | # admin_client set in BaseAdminNetworkTest but here we inherit from 34 | # BaseNetworkTest 35 | if not cls.admin_client: 36 | cls.admin_client = cls.os_admin.network_client 37 | 38 | @classmethod 39 | @utils.requires_ext(extension="router", service="network") 40 | def resource_setup(cls): 41 | super(FloatingIpTestCasesAdmin, cls).resource_setup() 42 | cls.network = cls.create_network() 43 | cls.create_subnet(cls.network) 44 | router = cls.create_router_by_client() 45 | cls.create_router_interface(router['id'], cls.subnets[0]['id']) 46 | # Create keypair with admin privileges 47 | cls.keypair = cls.create_keypair(client=cls.os_admin.keypairs_client) 48 | 49 | # Create security group with admin privileges 50 | network_client = cls.os_admin.network_client 51 | cls.secgroup = cls.create_security_group( 52 | client=cls.os_admin.network_client) 53 | cls.create_loginable_secgroup_rule( 54 | secgroup_id=cls.secgroup['id'], 55 | client=network_client) 56 | cls.create_pingable_secgroup_rule( 57 | secgroup_id=cls.secgroup['id'], 58 | client=network_client), 59 | 60 | def _list_hypervisors(self): 61 | # List of hypervisors 62 | return self.os_admin.hv_client.list_hypervisors()['hypervisors'] 63 | 64 | def _list_availability_zones(self): 65 | # List of availability zones 66 | func = self.os_admin.az_client.list_availability_zones 67 | return func()['availabilityZoneInfo'] 68 | 69 | def _create_vms(self, hyper, avail_zone, num_servers=2): 70 | servers, fips, server_ssh_clients = ([], [], []) 71 | # Create the availability zone with default zone and 72 | # a specific mentioned hypervisor. 73 | az = avail_zone + ':' + hyper 74 | for i in range(num_servers): 75 | servers.append(self.create_server( 76 | flavor_ref=CONF.compute.flavor_ref, 77 | image_ref=CONF.compute.image_ref, 78 | key_name=self.keypair['name'], 79 | networks=[{'uuid': self.network['id']}], 80 | security_groups=[{'name': self.secgroup['name']}], 81 | availability_zone=az)) 82 | for i, server in enumerate(servers): 83 | waiters.wait_for_server_status( 84 | self.os_admin.servers_client, server['server']['id'], 85 | const.SERVER_STATUS_ACTIVE) 86 | port = self.admin_client.list_ports( 87 | network_id=self.network['id'], 88 | device_id=server['server']['id'] 89 | )['ports'][0] 90 | fip = self.create_floatingip(port=port, 91 | client=self.os_admin.network_client) 92 | fips.append(fip) 93 | server_ssh_clients.append(ssh.Client( 94 | fips[i]['floating_ip_address'], CONF.validation.image_ssh_user, 95 | pkey=self.keypair['private_key'])) 96 | return servers, server_ssh_clients, fips 97 | 98 | @decorators.idempotent_id('6bba729b-3fb6-494b-9e1e-82bbd89a1045') 99 | def test_two_vms_fips(self): 100 | """Test two VMs floating IPs 101 | 102 | This test verifies the ability of two instances 103 | that were created in the same compute node and same availability zone 104 | to reach each other. 105 | """ 106 | # Get hypervisor list to pass it for vm creation 107 | hyper = self._list_hypervisors()[0]['hypervisor_hostname'] 108 | # Get availability zone list to pass it for vm creation 109 | avail_zone = self._list_availability_zones()[0]['zoneName'] 110 | servers, server_ssh_clients, fips = self._create_vms(hyper, avail_zone) 111 | self.check_remote_connectivity( 112 | server_ssh_clients[0], fips[1]['floating_ip_address'], 113 | servers=servers) 114 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/scenario/test_ports.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | import ipaddress 16 | 17 | from oslo_log import log as logging 18 | from tempest.common import waiters 19 | from tempest.lib.common.utils import data_utils 20 | from tempest.lib import decorators 21 | 22 | from neutron_tempest_plugin import config 23 | from neutron_tempest_plugin.scenario import base 24 | from neutron_tempest_plugin.scenario import constants as const 25 | 26 | LOG = logging.getLogger(__name__) 27 | CONF = config.CONF 28 | 29 | 30 | class PortsTest(base.BaseTempestTestCase): 31 | credentials = ['primary', 'admin'] 32 | 33 | @classmethod 34 | def resource_setup(cls): 35 | super(PortsTest, cls).resource_setup() 36 | # setup basic topology for servers we can log into it 37 | cls.router = cls.create_router_by_client() 38 | cls.keypair = cls.create_keypair() 39 | cls.secgroup = cls.create_security_group( 40 | name=data_utils.rand_name("test_port_secgroup")) 41 | cls.create_loginable_secgroup_rule( 42 | secgroup_id=cls.secgroup['id']) 43 | cls.create_pingable_secgroup_rule( 44 | secgroup_id=cls.secgroup['id']) 45 | cls.network = cls.create_network() 46 | cls.subnet = cls.create_subnet(cls.network) 47 | cls.create_router_interface(cls.router['id'], cls.subnet['id']) 48 | cls.port = cls.create_port(cls.network, 49 | name=data_utils.rand_name("port"), 50 | security_groups=[cls.secgroup['id']]) 51 | 52 | def _create_instance_with_port(self, port): 53 | """Create instance for port testing 54 | 55 | :param port (object): the port used 56 | """ 57 | servers, fips = ([], []) 58 | server_args = { 59 | 'flavor_ref': CONF.compute.flavor_ref, 60 | 'image_ref': CONF.compute.image_ref, 61 | 'key_name': self.keypair['name'], 62 | 'networks': [{'port': port['id']}] 63 | } 64 | servers.append(self.create_server(**server_args)) 65 | waiters.wait_for_server_status( 66 | self.os_primary.servers_client, servers[0]['server']['id'], 67 | const.SERVER_STATUS_ACTIVE) 68 | fips.append(self.create_floatingip(port=port)) 69 | return fips, servers 70 | 71 | @decorators.idempotent_id('5500797e-b8c2-4e07-a5e0-89fa4e814965') 72 | def test_previously_used_port(self): 73 | for i in range(2): 74 | fips, servers = self._create_instance_with_port( 75 | self.port) 76 | self.check_connectivity(fips[0]['floating_ip_address'], 77 | CONF.validation.image_ssh_user, 78 | self.keypair['private_key']) 79 | self.os_primary.servers_client.delete_server( 80 | servers[0]['server']['id']) 81 | waiters.wait_for_server_termination( 82 | self.os_primary.servers_client, 83 | servers[0]['server']['id']) 84 | self._try_delete_resource(self.delete_floatingip, fips[0]) 85 | 86 | @decorators.idempotent_id('62e32802-1d21-11eb-b322-74e5f9e2a801') 87 | def test_port_with_fixed_ip(self): 88 | """Test scenario: 89 | 90 | 1) Get the last IP from the range of Subnet "Allocation pool" 91 | 2) Create Port with fixed_ip resolved in #1 92 | 3) Create a VM using updated Port in #2 and add Floating IP 93 | 4) Check SSH access to VM 94 | """ 95 | ip_range = [str(ip) for ip in ipaddress.IPv4Network( 96 | self.subnet['cidr'])] 97 | # Because of the tests executed in Parallel the IP may already 98 | # be in use, so we'll try using IPs from Allocation pool 99 | # (in reverse order) up until Port is successfully created. 100 | for ip in reversed(ip_range): 101 | try: 102 | port = self.create_port( 103 | self.network, 104 | name=data_utils.rand_name("fixed_ip_port"), 105 | security_groups=[self.secgroup['id']], 106 | fixed_ips=[{'ip_address': ip}]) 107 | if port is not None: 108 | break 109 | except Exception as e: 110 | LOG.warning('Failed to create Port, using Fixed_IP:%s, ' 111 | 'the Error was:%s', ip, e) 112 | fip, server = self._create_instance_with_port(port) 113 | self.check_connectivity(fip[0]['floating_ip_address'], 114 | CONF.validation.image_ssh_user, 115 | self.keypair['private_key']) 116 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_ndp_proxy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Troila 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from neutron_lib import constants 17 | from tempest.lib.common.utils import data_utils 18 | from tempest.lib import decorators 19 | from tempest.lib import exceptions 20 | 21 | from neutron_tempest_plugin.api import base 22 | from neutron_tempest_plugin import config 23 | 24 | CONF = config.CONF 25 | 26 | 27 | class NDPProxyTestJSON(base.BaseAdminNetworkTest): 28 | 29 | credentials = ['primary', 'admin'] 30 | required_extensions = ['router', 'l3-ndp-proxy', 'address-scope'] 31 | 32 | @classmethod 33 | def resource_setup(cls): 34 | super(NDPProxyTestJSON, cls).resource_setup() 35 | address_scope = cls.create_address_scope( 36 | "test-as", **{'ip_version': constants.IP_VERSION_6}) 37 | subnetpool = cls.create_subnetpool( 38 | "test-subnetpool", 39 | **{'address_scope_id': address_scope['id'], 40 | 'default_prefixlen': 112, 41 | 'prefixes': ['2001:abc::0/96']}) 42 | # Create an external network and it's subnet 43 | ext_net = cls.create_network('test-ext-net', client=cls.admin_client, 44 | external=True) 45 | cls.create_subnet( 46 | ext_net, client=cls.admin_client, 47 | ip_version=constants.IP_VERSION_6, 48 | **{'subnetpool_id': subnetpool['id'], "cidr": "2001:abc::1:0/112"}) 49 | # Create network, subnet, router and add interface 50 | cls.network = cls.create_network() 51 | cls.subnet = cls.create_subnet( 52 | cls.network, ip_version=constants.IP_VERSION_6, 53 | **{'subnetpool_id': subnetpool['id'], "cidr": "2001:abc::2:0/112"}) 54 | cls.router = cls.create_router(data_utils.rand_name('router'), 55 | external_network_id=ext_net['id'], 56 | enable_ndp_proxy=True) 57 | cls.create_router_interface(cls.router['id'], cls.subnet['id']) 58 | 59 | @decorators.idempotent_id('481bc712-d504-4128-bffb-62d98b88886b') 60 | def test_ndp_proxy_lifecycle(self): 61 | port = self.create_port(self.network) 62 | np_description = 'Test ndp proxy description' 63 | np_name = 'test-ndp-proxy' 64 | 65 | # Create ndp proxy 66 | created_ndp_proxy = self.create_ndp_proxy( 67 | name=np_name, 68 | description=np_description, 69 | router_id=self.router['id'], 70 | port_id=port['id']) 71 | self.assertEqual(self.router['id'], created_ndp_proxy['router_id']) 72 | self.assertEqual(port['id'], created_ndp_proxy['port_id']) 73 | self.assertEqual(np_description, created_ndp_proxy['description']) 74 | self.assertEqual(np_name, created_ndp_proxy['name']) 75 | self.assertEqual(port['fixed_ips'][0]['ip_address'], 76 | created_ndp_proxy['ip_address']) 77 | 78 | # Show created ndp_proxy 79 | body = self.client.get_ndp_proxy(created_ndp_proxy['id']) 80 | ndp_proxy = body['ndp_proxy'] 81 | self.assertEqual(np_description, ndp_proxy['description']) 82 | self.assertEqual(self.router['id'], ndp_proxy['router_id']) 83 | self.assertEqual(port['id'], ndp_proxy['port_id']) 84 | self.assertEqual(np_name, ndp_proxy['name']) 85 | self.assertEqual(port['fixed_ips'][0]['ip_address'], 86 | ndp_proxy['ip_address']) 87 | 88 | # List ndp proxies 89 | body = self.client.list_ndp_proxies() 90 | ndp_proxy_ids = [np['id'] for np in body['ndp_proxies']] 91 | self.assertIn(created_ndp_proxy['id'], ndp_proxy_ids) 92 | 93 | # Update ndp proxy 94 | updated_ndp_proxy = self.client.update_ndp_proxy( 95 | created_ndp_proxy['id'], 96 | name='updated_ndp_proxy') 97 | self.assertEqual('updated_ndp_proxy', 98 | updated_ndp_proxy['ndp_proxy']['name']) 99 | self.assertEqual( 100 | np_description, updated_ndp_proxy['ndp_proxy']['description']) 101 | self.assertEqual(self.router['id'], 102 | updated_ndp_proxy['ndp_proxy']['router_id']) 103 | self.assertEqual(port['id'], updated_ndp_proxy['ndp_proxy']['port_id']) 104 | self.assertEqual(port['fixed_ips'][0]['ip_address'], 105 | updated_ndp_proxy['ndp_proxy']['ip_address']) 106 | 107 | # Delete ndp proxy 108 | self.delete_ndp_proxy(created_ndp_proxy) 109 | self.assertRaises(exceptions.NotFound, 110 | self.client.get_ndp_proxy, created_ndp_proxy['id']) 111 | -------------------------------------------------------------------------------- /neutron_tempest_plugin/api/test_port_forwarding_negative.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 OpenStack Foundation 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from tempest.lib.common.utils import data_utils 17 | from tempest.lib import decorators 18 | from tempest.lib import exceptions 19 | 20 | from neutron_tempest_plugin.api import base 21 | from neutron_tempest_plugin import config 22 | 23 | CONF = config.CONF 24 | 25 | 26 | class PortForwardingNegativeTestJSON(base.BaseNetworkTest): 27 | required_extensions = ['router', 'floating-ip-port-forwarding'] 28 | 29 | @classmethod 30 | def resource_setup(cls): 31 | super(PortForwardingNegativeTestJSON, cls).resource_setup() 32 | cls.ext_net_id = CONF.network.public_network_id 33 | 34 | # Create network, subnet, router and add interface 35 | cls.network = cls.create_network() 36 | cls.subnet = cls.create_subnet(cls.network) 37 | cls.router = cls.create_router(data_utils.rand_name('router'), 38 | external_network_id=cls.ext_net_id) 39 | cls.create_router_interface(cls.router['id'], cls.subnet['id']) 40 | 41 | @decorators.attr(type='negative') 42 | @decorators.idempotent_id('63c0d406-99d5-11ea-bb37-0242ac130002') 43 | def test_mapping_same_fip_and_external_port_to_different_dest(self): 44 | port1 = self.create_port(self.network) 45 | port2 = self.create_port(self.network) 46 | fip_for_pf = self.create_floatingip() 47 | 48 | self.create_port_forwarding( 49 | fip_for_pf['id'], 50 | internal_port_id=port1['id'], 51 | internal_ip_address=port1['fixed_ips'][0]['ip_address'], 52 | internal_port=1111, external_port=2222, 53 | protocol="tcp") 54 | 55 | self.assertRaises( 56 | exceptions.BadRequest, 57 | self.create_port_forwarding, 58 | fip_for_pf['id'], 59 | internal_port_id=port2['id'], 60 | internal_ip_address=port2['fixed_ips'][0]['ip_address'], 61 | internal_port=3333, external_port=2222, 62 | protocol="tcp") 63 | 64 | @decorators.attr(type='negative') 65 | @decorators.idempotent_id('0c229a4c-9f28-11ea-bb37-0242ac130002') 66 | def test_mapping_different_external_ports_to_the_same_destination(self): 67 | port = self.create_port(self.network) 68 | fip_for_pf = self.create_floatingip() 69 | 70 | self.create_port_forwarding( 71 | fip_for_pf['id'], 72 | internal_port_id=port['id'], 73 | internal_ip_address=port['fixed_ips'][0]['ip_address'], 74 | internal_port=1111, external_port=3333, 75 | protocol="tcp") 76 | 77 | self.assertRaises( 78 | exceptions.BadRequest, 79 | self.create_port_forwarding, 80 | fip_for_pf['id'], 81 | internal_port_id=port['id'], 82 | internal_ip_address=port['fixed_ips'][0]['ip_address'], 83 | internal_port=1111, external_port=5555, 84 | protocol="tcp") 85 | 86 | @decorators.attr(type='negative') 87 | @decorators.idempotent_id('e9d3ffb6-e5bf-421d-acaa-ee6010dfbf14') 88 | def test_out_of_range_ports(self): 89 | port = self.create_port(self.network) 90 | fip_for_pf = self.create_floatingip() 91 | 92 | pf_params = { 93 | 'internal_port_id': port['id'], 94 | 'internal_ip_address': port['fixed_ips'][0]['ip_address'], 95 | 'internal_port': 1111, 96 | 'external_port': 3333, 97 | 'protocol': "tcp"} 98 | pf = self.create_port_forwarding(fip_for_pf['id'], **pf_params) 99 | 100 | # Check: Invalid input for external_port update 101 | self.assertRaises( 102 | exceptions.BadRequest, 103 | self.update_port_forwarding, 104 | fip_for_pf['id'], pf['id'], external_port=108343) 105 | 106 | # Check: Invalid input for internal_port update 107 | self.assertRaises( 108 | exceptions.BadRequest, 109 | self.update_port_forwarding, 110 | fip_for_pf['id'], pf['id'], internal_port=108343) 111 | 112 | # Check: Invalid input for external_port create 113 | pf_params['internal_port'] = 4444 114 | pf_params['external_port'] = 333333 115 | self.assertRaises( 116 | exceptions.BadRequest, 117 | self.create_port_forwarding, fip_for_pf['id'], **pf_params) 118 | 119 | # Check: Invalid input for internal_port create 120 | pf_params['internal_port'] = 444444 121 | pf_params['external_port'] = 3333 122 | self.assertRaises( 123 | exceptions.BadRequest, 124 | self.create_port_forwarding, fip_for_pf['id'], **pf_params) 125 | --------------------------------------------------------------------------------