Merge "Make connectivity test less backend dependent"
diff --git a/.zuul.yaml b/.zuul.yaml
index c5f7ecc..9af79a6 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -51,6 +51,41 @@
ENABLE_FILE_INJECTION: true
- job:
+ name: devstack-tempest-ipv6
+ parent: devstack-ipv6
+ nodeset: openstack-single-node
+ description: |
+ Base Tempest IPv6 job.
+ required-projects:
+ - git.openstack.org/openstack/tempest
+ timeout: 7200
+ roles:
+ - zuul: git.openstack.org/openstack-dev/devstack
+ vars:
+ devstack_services:
+ tempest: true
+ devstack_local_conf:
+ test-config:
+ $TEMPEST_CONFIG:
+ compute:
+ min_compute_nodes: "{{ groups['compute'] | default(['controller']) | length }}"
+ test_results_stage_name: test_results
+ zuul_copy_output:
+ '{{ devstack_base_dir }}/tempest/etc/tempest.conf': logs
+ '{{ devstack_base_dir }}/tempest/etc/accounts.yaml': logs
+ '{{ devstack_base_dir }}/tempest/tempest.log': logs
+ '{{ stage_dir }}/{{ test_results_stage_name }}.subunit': logs
+ '{{ stage_dir }}/{{ test_results_stage_name }}.html': logs
+ '{{ stage_dir }}/stackviz': logs
+ extensions_to_txt:
+ conf: true
+ log: true
+ yaml: true
+ yml: true
+ run: playbooks/devstack-tempest.yaml
+ post-run: playbooks/post-tempest.yaml
+
+- job:
name: tempest-full
parent: devstack-tempest
# This currently works from stable/pike on.
@@ -68,6 +103,37 @@
ENABLE_FILE_INJECTION: true
- job:
+ name: tempest-full-oslo-master
+ parent: tempest-full
+ description: |
+ Integration test using current git of oslo libs.
+ This ensures that when oslo libs get released that they
+ do not break OpenStack server projects.
+
+ Former name for this job was
+ periodic-tempest-dsvm-oslo-latest-full-master.
+ timeout: 10800
+ required-projects:
+ - git.openstack.org/openstack/oslo.cache
+ - git.openstack.org/openstack/oslo.concurrency
+ - git.openstack.org/openstack/oslo.config
+ - git.openstack.org/openstack/oslo.context
+ - git.openstack.org/openstack/oslo.db
+ - git.openstack.org/openstack/oslo.i18n
+ - git.openstack.org/openstack/oslo.log
+ - git.openstack.org/openstack/oslo.messaging
+ - git.openstack.org/openstack/oslo.middleware
+ - git.openstack.org/openstack/oslo.policy
+ - git.openstack.org/openstack/oslo.privsep
+ - git.openstack.org/openstack/oslo.reports
+ - git.openstack.org/openstack/oslo.rootwrap
+ - git.openstack.org/openstack/oslo.serialization
+ - git.openstack.org/openstack/oslo.service
+ - git.openstack.org/openstack/oslo.utils
+ - git.openstack.org/openstack/oslo.versionedobjects
+ - git.openstack.org/openstack/oslo.vmware
+
+- job:
name: tempest-full-parallel
parent: tempest-full
voting: false
@@ -106,6 +172,28 @@
c-bak: false
- job:
+ name: tempest-full-py3-ipv6
+ parent: devstack-tempest-ipv6
+ # This currently works from stable/pike on.
+ # Before stable/pike, legacy version of tempest-full
+ # 'legacy-tempest-dsvm-neutron-full' run.
+ branches: ^(?!stable/ocata).*$
+ description: |
+ Base integration test with Neutron networking, IPv6 and py3.
+ vars:
+ tox_envlist: full
+ devstack_localrc:
+ USE_PYTHON3: true
+ FORCE_CONFIG_DRIVE: true
+ devstack_services:
+ s-account: false
+ s-container: false
+ s-object: false
+ s-proxy: false
+ # without Swift, c-bak cannot run (in the Gate at least)
+ c-bak: false
+
+- job:
name: tempest-multinode-full
parent: devstack-tempest
nodeset: openstack-two-node
@@ -238,22 +326,26 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
required-projects:
+ - git.openstack.org/openstack/airship-tempest-plugin
- git.openstack.org/openstack/almanach
- git.openstack.org/openstack/aodh
- git.openstack.org/openstack/barbican-tempest-plugin
+ - git.openstack.org/openstack/blazar-tempest-plugin
- git.openstack.org/openstack/ceilometer
- - git.openstack.org/openstack/cinder
- - git.openstack.org/openstack/congress
+ - git.openstack.org/openstack/cinder-tempest-plugin
+ - git.openstack.org/openstack/cloudkitty-tempest-plugin
+ - git.openstack.org/openstack/congress-tempest-plugin
- git.openstack.org/openstack/designate-tempest-plugin
- - git.openstack.org/openstack/ec2-api
+ - git.openstack.org/openstack/ec2api-tempest-plugin
- git.openstack.org/openstack/freezer
- git.openstack.org/openstack/freezer-api
- git.openstack.org/openstack/freezer-tempest-plugin
+ - git.openstack.org/openstack/gabbi-tempest
- git.openstack.org/openstack/gce-api
- git.openstack.org/openstack/glare
- - git.openstack.org/openstack/heat
+ - git.openstack.org/openstack/heat-tempest-plugin
- git.openstack.org/openstack/intel-nfv-ci-tests
- - git.openstack.org/openstack/ironic
+ - git.openstack.org/openstack/ironic-tempest-plugin
- git.openstack.org/openstack/ironic-inspector
- git.openstack.org/openstack/keystone-tempest-plugin
- git.openstack.org/openstack/kingbird
@@ -262,19 +354,21 @@
- git.openstack.org/openstack/magnum-tempest-plugin
- git.openstack.org/openstack/manila
- git.openstack.org/openstack/manila-tempest-plugin
- - git.openstack.org/openstack/mistral
+ - git.openstack.org/openstack/mistral-tempest-plugin
- git.openstack.org/openstack/mogan
- git.openstack.org/openstack/monasca-api
- git.openstack.org/openstack/monasca-log-api
- - git.openstack.org/openstack/murano
+ - git.openstack.org/openstack/monasca-tempest-plugin
+ - git.openstack.org/openstack/murano-tempest-plugin
+ - git.openstack.org/openstack/networking-ansible
- git.openstack.org/openstack/networking-bgpvpn
- git.openstack.org/openstack/networking-cisco
- git.openstack.org/openstack/networking-fortinet
- git.openstack.org/openstack/networking-generic-switch
- - git.openstack.org/openstack/networking-l2gw
+ - git.openstack.org/openstack/networking-l2gw-tempest-plugin
- git.openstack.org/openstack/networking-midonet
- - git.openstack.org/openstack/networking-plumgrid
- git.openstack.org/openstack/networking-sfc
+ - git.openstack.org/openstack/networking-spp
- git.openstack.org/openstack/neutron
- git.openstack.org/openstack/neutron-dynamic-routing
- git.openstack.org/openstack/neutron-fwaas
@@ -283,21 +377,25 @@
- git.openstack.org/openstack/neutron-vpnaas
- git.openstack.org/openstack/nova-lxd
- git.openstack.org/openstack/novajoin-tempest-plugin
+ - git.openstack.org/openstack/octavia
- git.openstack.org/openstack/octavia-tempest-plugin
- git.openstack.org/openstack/oswin-tempest-plugin
- git.openstack.org/openstack/panko
- git.openstack.org/openstack/patrole
+ - git.openstack.org/openstack/python-watcherclient
- git.openstack.org/openstack/qinling
- git.openstack.org/openstack/requirements
- git.openstack.org/openstack/sahara-tests
- git.openstack.org/openstack/senlin
- git.openstack.org/openstack/senlin-tempest-plugin
- git.openstack.org/openstack/tap-as-a-service
+ - git.openstack.org/openstack/telemetry-tempest-plugin
- git.openstack.org/openstack/tempest-horizon
- - git.openstack.org/openstack/trio2o
- - git.openstack.org/openstack/trove
+ - git.openstack.org/openstack/tobiko
+ - git.openstack.org/openstack/tripleo-common-tempest-plugin
+ - git.openstack.org/openstack/trove-tempest-plugin
- git.openstack.org/openstack/valet
- - git.openstack.org/openstack/vitrage
+ - git.openstack.org/openstack/vitrage-tempest-plugin
- git.openstack.org/openstack/vmware-nsx-tempest-plugin
- git.openstack.org/openstack/watcher-tempest-plugin
- git.openstack.org/openstack/zaqar-tempest-plugin
@@ -372,6 +470,12 @@
- ^playbooks/
- ^roles/
- ^.zuul.yaml$
+ - devstack-tempest-ipv6:
+ voting: false
+ files:
+ - ^playbooks/
+ - ^roles/
+ - ^.zuul.yaml$
- nova-multiattach:
# Define list of irrelevant files to use everywhere else
irrelevant-files: &tempest-irrelevant-files
@@ -383,10 +487,16 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
+ - ^tools/.*$
- tempest-full-parallel:
irrelevant-files: *tempest-irrelevant-files
+ - tempest-full-py3:
+ irrelevant-files: *tempest-irrelevant-files
- tempest-full-py36:
irrelevant-files: *tempest-irrelevant-files
+ - tempest-full-py3-ipv6:
+ voting: false
+ irrelevant-files: *tempest-irrelevant-files
- tempest-full-rocky:
irrelevant-files: *tempest-irrelevant-files
- tempest-full-rocky-py3:
@@ -399,7 +509,17 @@
irrelevant-files: *tempest-irrelevant-files
- tempest-multinode-full:
irrelevant-files: *tempest-irrelevant-files
- - tempest-tox-plugin-sanity-check
+ - tempest-tox-plugin-sanity-check:
+ irrelevant-files:
+ - ^(test-|)requirements.txt$
+ - ^.*\.rst$
+ - ^doc/.*$
+ - ^etc/.*$
+ - ^releasenotes/.*$
+ - ^setup.cfg$
+ - ^tempest/hacking/.*$
+ - ^tempest/tests/.*$
+ # tools/ is not here since this relies on a script in tools/.
- tempest-slow:
irrelevant-files: *tempest-irrelevant-files
- nova-cells-v1:
@@ -459,8 +579,6 @@
irrelevant-files: *tempest-irrelevant-files
- tempest-all:
irrelevant-files: *tempest-irrelevant-files
- - legacy-tempest-dsvm-multinode-full:
- irrelevant-files: *tempest-irrelevant-files
- legacy-tempest-dsvm-neutron-dvr-multinode-full:
irrelevant-files: *tempest-irrelevant-files
- neutron-tempest-dvr-ha-multinode-full:
@@ -486,3 +604,4 @@
periodic:
jobs:
- tempest-all
+ - tempest-full-oslo-master
diff --git a/HACKING.rst b/HACKING.rst
index e767b25..e8791f8 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -35,6 +35,30 @@
- Clean up test data at the completion of each test
- Use configuration files for values that will vary by environment
+Supported OpenStack Components
+------------------------------
+
+Tempest's :ref:`library` and :ref:`plugin interface <tempest_plugin>` can be
+leveraged to support integration testing for virtually any OpenStack component.
+
+However, Tempest only offers **in-tree** integration testing coverage for the
+following components:
+
+* Cinder
+* Glance
+* Keystone
+* Neutron
+* Nova
+* Swift
+
+Historically, Tempest offered in-tree testing for other components as well, but
+since the introduction of the `External Plugin Interface`_, Tempest's in-tree
+testing scope has been limited to the projects above. Integration tests for
+projects not included above should go into one of the
+`relevant plugin projects`_.
+
+.. _External Plugin Interface: https://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/tempest-external-plugin-interface.html
+.. _relevant plugin projects: https://docs.openstack.org/tempest/latest/plugin-registry.html#detected-plugins
Exception Handling
------------------
@@ -431,7 +455,7 @@
by modifying Tempest's `lib installation script`_ for previous branches
(because DevStack is branched).
-.. _lib installation script: http://git.openstack.org/cgit/openstack-dev/devstack/tree/lib/tempest
+.. _lib installation script: https://git.openstack.org/cgit/openstack-dev/devstack/tree/lib/tempest
2. Bug fix on core project needing Tempest changes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index 307ceb3..73930f1 100644
--- a/README.rst
+++ b/README.rst
@@ -165,7 +165,7 @@
interface and when Z is incremented it's a bug fix release for the library.
Also note that both Y and Z are reset to 0 at each increment of X.
-.. _semver: http://semver.org/
+.. _semver: https://semver.org/
Configuration
-------------
@@ -218,7 +218,7 @@
argument is no longer required, however it may perform faster if included.
For more information on these options and details about stestr, please see the
-`stestr documentation <http://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
+`stestr documentation <https://stestr.readthedocs.io/en/latest/MANUAL.html>`_.
Python 3.x
----------
diff --git a/REVIEWING.rst b/REVIEWING.rst
index bf63ed2..31fedce 100644
--- a/REVIEWING.rst
+++ b/REVIEWING.rst
@@ -159,8 +159,32 @@
When to approve
---------------
-* Every patch needs two +2s before being approved.
-* It's ok to hold off on an approval until a subject matter expert reviews it
-* If a patch has already been approved but requires a trivial rebase to merge,
- you do not have to wait for a second +2, since the patch has already had
- two +2s.
+* It's OK to hold off on an approval until a subject matter expert reviews it.
+* Every patch needs two +2's before being approved.
+* However, a single Tempest core reviewer can approve patches without waiting
+ for another +2 in the following cases:
+
+ * If a patch has already been approved but requires a trivial rebase to
+ merge, then there is no need to wait for a second +2, since the patch has
+ already had two +2's.
+ * If any trivial patch set fixes one of the items below:
+
+ * Documentation or code comment typo
+ * Documentation ref link
+ * Example: `example`_
+
+ .. note::
+
+ Any other small documentation, CI job, or code change does not fall under
+ this category.
+
+ * If the patch **unblocks** a failing project gate, provided that:
+
+ * the project's PTL +1's the change
+ * the patch does not affect any other project's testing gates
+ * the patch does not cause any negative side effects
+
+ Note that such a policy should be used judiciously, as we should strive to
+ have two +2's on each patch set, prior to approval.
+
+.. _example: https://review.openstack.org/#/c/611032/
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index 9958792..dc0e94c 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -96,7 +96,7 @@
that users don't have to worry about inadvertently installing a Tempest plugin
when they install another package.
-.. _Branchless Tempest Spec: http://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/branchless-tempest.html
+.. _Branchless Tempest Spec: https://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/branchless-tempest.html
The sole advantage to integrating a plugin into an existing python project is
that it enables you to land code changes at the same time you land test changes
diff --git a/playbooks/devstack-tempest.yaml b/playbooks/devstack-tempest.yaml
index 01155a8..b51e701 100644
--- a/playbooks/devstack-tempest.yaml
+++ b/playbooks/devstack-tempest.yaml
@@ -7,6 +7,11 @@
# We run tests only on one node, regardless how many nodes are in the system
- hosts: tempest
+ environment:
+ # This enviroment variable is used by the optional tempest-gabbi
+ # job provided by the gabbi-tempest plugin. It can be safely ignored
+ # if that plugin is not being used.
+ GABBI_TEMPEST_PATH: "{{ gabbi_tempest_path }}"
roles:
- setup-tempest-run-dir
- setup-tempest-data-dir
diff --git a/releasenotes/notes/bug-1799883-6ea95fc64f70c9ef.yaml b/releasenotes/notes/bug-1799883-6ea95fc64f70c9ef.yaml
new file mode 100644
index 0000000..630908f
--- /dev/null
+++ b/releasenotes/notes/bug-1799883-6ea95fc64f70c9ef.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+ - |
+ Fixed bug #1799883. ``tempest workspace register`` and ``tempest workspace move`` CLI
+ will now validate the value of the ``--path`` CLI argument and raise an exception if
+ it is None or empty string. Earlier both CLI actions were accepting None or empty path
+ which was confusing.
diff --git a/releasenotes/notes/deprecate-scheduler-available-filters-cbca2017ba3cf2aa.yaml b/releasenotes/notes/deprecate-scheduler-available-filters-cbca2017ba3cf2aa.yaml
new file mode 100644
index 0000000..d0c3a7d
--- /dev/null
+++ b/releasenotes/notes/deprecate-scheduler-available-filters-cbca2017ba3cf2aa.yaml
@@ -0,0 +1,13 @@
+---
+deprecations:
+ - |
+ The ``scheduler_available_filters`` option is being deprecated in favor of
+ ``scheduler_enabled_filters``. The new name is more indicative of what the
+ option means. ``scheduler_enabled_filters``'s default value is set to the
+ default value of Nova's ``enabled_filters``.
+ ``scheduler_available_filters``'s default was `all`. There was confusion
+ around this value. Sometimes it was understood to mean the default Nova
+ filters are enabled, other times it was understood to mean all filters are
+ enabled. While `all` is still allowed for ``scheduler_enabled_filters`` for
+ backwards compatibility, it is strongly recommended to provide an explicit
+ list of filters that matches what's configured in nova.conf.
diff --git a/releasenotes/notes/remove-deprecated-find-test-caller-f4525cd043bfd1b6.yaml b/releasenotes/notes/remove-deprecated-find-test-caller-f4525cd043bfd1b6.yaml
new file mode 100644
index 0000000..f22736f
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-find-test-caller-f4525cd043bfd1b6.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+ - |
+ ``tempest.lib.common.utils.misc.find_test_caller`` was deprecated during
+ Kilo release cycle in favor of
+ ``tempest.lib.common.utils.test_utils.find_test_caller``. The deprecated
+ version of ``find_test_caller`` is removed.
diff --git a/roles/run-tempest/README.rst b/roles/run-tempest/README.rst
index 71b8e4f..e1787b6 100644
--- a/roles/run-tempest/README.rst
+++ b/roles/run-tempest/README.rst
@@ -56,3 +56,14 @@
(?x) # Ignore comments and whitespaces
# Line with only a comment.
(tempest.api.identity).*$
+
+.. zuul:rolevar:: tox_extra_args
+ :default: ''
+
+ String of extra command line options to pass to tox.
+
+ Here is an example of running tox with --sitepackages option:
+
+ ::
+ vars:
+ tox_extra_args: --sitepackages
diff --git a/roles/run-tempest/defaults/main.yaml b/roles/run-tempest/defaults/main.yaml
index c89eb93..06918b5 100644
--- a/roles/run-tempest/defaults/main.yaml
+++ b/roles/run-tempest/defaults/main.yaml
@@ -2,3 +2,4 @@
tempest_test_regex: ''
tox_envlist: smoke
tempest_black_regex: ''
+tox_extra_args: ''
diff --git a/roles/run-tempest/tasks/main.yaml b/roles/run-tempest/tasks/main.yaml
index 54ddc71..16086aa 100644
--- a/roles/run-tempest/tasks/main.yaml
+++ b/roles/run-tempest/tasks/main.yaml
@@ -35,7 +35,7 @@
when: blacklist_stat.stat.exists
- name: Run Tempest
- command: tox -e {{tox_envlist}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} \
+ command: tox -e {{tox_envlist}} {{tox_extra_args}} -- {{tempest_test_regex|quote}} {{blacklist_option|default('')}} \
--concurrency={{tempest_concurrency|default(default_concurrency)}} \
--black-regex={{tempest_black_regex|quote}}
args:
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index f0178aa..dfa801b 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -73,8 +73,17 @@
# search filter
fetched_list = (self.client.list_security_groups(all_tenants='true')
['security_groups'])
- # Now check if all created Security Groups are present in fetched list
- for sec_group in fetched_list:
- self.assertEqual(sec_group['tenant_id'], client_tenant_id,
- "Failed to get all security groups for "
- "non admin user.")
+ sec_group_id_list = [sg['id'] for sg in fetched_list]
+ # Now check that 'all_tenants='true' filter for non-admin user only
+ # provide the requested non-admin user's created security groups,
+ # not all security groups which include security groups created by
+ # other users.
+ for sec_group in security_group_list:
+ if sec_group['tenant_id'] == client_tenant_id:
+ self.assertIn(sec_group['id'], sec_group_id_list,
+ "Failed to get all security groups for "
+ "non admin user.")
+ else:
+ self.assertNotIn(sec_group['id'], sec_group_id_list,
+ "Non admin user shouldn't get other user's "
+ "security groups.")
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 2a5d607..05c2a28 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -24,6 +24,7 @@
from tempest.common import waiters
from tempest import config
from tempest.lib.common.utils.linux import remote_client
+from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
@@ -168,7 +169,9 @@
iface = self.interfaces_client.create_interface(
server['id'], net_id=network_id,
fixed_ips=fixed_ips)['interfaceAttachment']
- self.addCleanup(self.ports_client.delete_port, iface['port_id'])
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.ports_client.delete_port,
+ iface['port_id'])
self._check_interface(iface, server_id=server['id'],
fixed_ip=ip_list[0])
return iface
@@ -260,16 +263,6 @@
interface_count = len(ifs)
self.assertGreater(interface_count, 0)
- try:
- iface = self._test_create_interface(server)
- except lib_exc.BadRequest as e:
- msg = ('Multiple possible networks found, use a Network ID to be '
- 'more specific.')
- if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
- raise
- else:
- ifs.append(iface)
-
iface = self._test_create_interface_by_fixed_ips(server, ifs)
ifs.append(iface)
@@ -340,20 +333,61 @@
def test_add_remove_fixed_ip(self):
# Add and Remove the fixed IP to server.
server, ifs = self._create_server_get_interfaces()
- interface_count = len(ifs)
- self.assertGreater(interface_count, 0)
+ original_interface_count = len(ifs) # This is the number of ports.
+ self.assertGreater(original_interface_count, 0)
+ # Get the starting list of IPs on the server.
+ addresses = self.os_primary.servers_client.list_addresses(
+ server['id'])['addresses']
+ # There should be one entry for the single network mapped to a list of
+ # addresses, which at this point should have at least one entry.
+ # Note that we could start with two addresses depending on how tempest
+ # is configured for using floating IPs.
+ self.assertEqual(1, len(addresses), addresses) # number of networks
+ # Keep track of the original addresses so we can know which IP is new.
+ original_ips = [addr['addr'] for addr in list(addresses.values())[0]]
+ original_ip_count = len(original_ips)
+ self.assertGreater(original_ip_count, 0, addresses) # at least 1
network_id = ifs[0]['net_id']
+ # Add another fixed IP to the server. This should result in another
+ # fixed IP on the same network (and same port since we only have one
+ # port).
self.servers_client.add_fixed_ip(server['id'], networkId=network_id)
- # Remove the fixed IP from server.
+ # Wait for the ips count to increase by one.
+
+ def _wait_for_ip_increase():
+ _addresses = self.os_primary.servers_client.list_addresses(
+ server['id'])['addresses']
+ return len(list(_addresses.values())[0]) == original_ip_count + 1
+
+ if not test_utils.call_until_true(
+ _wait_for_ip_increase, CONF.compute.build_timeout,
+ CONF.compute.build_interval):
+ raise lib_exc.TimeoutException(
+ 'Timed out while waiting for IP count to increase.')
+
+ # Remove the fixed IP that we just added.
server_detail = self.os_primary.servers_client.show_server(
server['id'])['server']
# Get the Fixed IP from server.
fixed_ip = None
for ip_set in server_detail['addresses']:
for ip in server_detail['addresses'][ip_set]:
- if ip['OS-EXT-IPS:type'] == 'fixed':
+ if (ip['OS-EXT-IPS:type'] == 'fixed' and
+ ip['addr'] not in original_ips):
fixed_ip = ip['addr']
break
if fixed_ip is not None:
break
self.servers_client.remove_fixed_ip(server['id'], address=fixed_ip)
+ # Wait for the interface count to decrease by one.
+
+ def _wait_for_ip_decrease():
+ _addresses = self.os_primary.servers_client.list_addresses(
+ server['id'])['addresses']
+ return len(list(_addresses.values())[0]) == original_ip_count
+
+ if not test_utils.call_until_true(
+ _wait_for_ip_decrease, CONF.compute.build_timeout,
+ CONF.compute.build_interval):
+ raise lib_exc.TimeoutException(
+ 'Timed out while waiting for IP count to decrease.')
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index b0ef3bc..6629794 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -24,11 +24,11 @@
CONF = config.CONF
-class ServerRescueTestJSON(base.BaseV2ComputeTest):
+class ServerRescueTestBase(base.BaseV2ComputeTest):
@classmethod
def skip_checks(cls):
- super(ServerRescueTestJSON, cls).skip_checks()
+ super(ServerRescueTestBase, cls).skip_checks()
if not CONF.compute_feature_enabled.rescue:
msg = "Server rescue not available."
raise cls.skipException(msg)
@@ -36,11 +36,11 @@
@classmethod
def setup_credentials(cls):
cls.set_network_resources(network=True, subnet=True, router=True)
- super(ServerRescueTestJSON, cls).setup_credentials()
+ super(ServerRescueTestBase, cls).setup_credentials()
@classmethod
def resource_setup(cls):
- super(ServerRescueTestJSON, cls).resource_setup()
+ super(ServerRescueTestBase, cls).resource_setup()
password = data_utils.rand_password()
server = cls.create_test_server(adminPass=password,
@@ -50,6 +50,9 @@
'RESCUE')
cls.rescued_server_id = server['id']
+
+class ServerRescueTestJSON(ServerRescueTestBase):
+
@decorators.idempotent_id('fd032140-714c-42e4-a8fd-adcd8df06be6')
def test_rescue_unrescue_instance(self):
password = data_utils.rand_password()
@@ -62,6 +65,15 @@
waiters.wait_for_server_status(self.servers_client, server['id'],
'ACTIVE')
+
+class ServerRescueTestJSONUnderV235(ServerRescueTestBase):
+
+ max_microversion = '2.35'
+
+ # TODO(zhufl): After 2.35 we should switch to neutron client to create
+ # floating ip, but that will need admin credential, so the testcases will
+ # have to be added in somewhere in admin directory.
+
@decorators.idempotent_id('4842e0cf-e87d-4d9d-b61f-f4791da3cacc')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index e2ed5ef..03543ac 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -89,14 +89,10 @@
service = self.services_client.create_service(
name=name, type=s_type,
description=description)['OS-KSADM:service']
+ self.addCleanup(self.services_client.delete_service, service['id'])
services.append(service)
service_ids = [svc['id'] for svc in services]
- def delete_services():
- for service_id in service_ids:
- self.services_client.delete_service(service_id)
-
- self.addCleanup(delete_services)
# List and Verify Services
body = self.services_client.list_services()['OS-KSADM:services']
found = [serv for serv in body if serv['id'] in service_ids]
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index cda721c..f68754e 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -50,7 +50,7 @@
'been sent in response for create')
body = self.tenants_client.show_tenant(tenant_id)['tenant']
desc2 = body['description']
- self.assertEqual(desc2, tenant_desc, 'Description does not appear'
+ self.assertEqual(desc2, tenant_desc, 'Description does not appear '
'to be set')
self.tenants_client.delete_tenant(tenant_id)
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 6ddf42e..f75edaa 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -31,7 +31,7 @@
'been sent in response for create')
body = self.projects_client.show_project(project_id)['project']
desc2 = body['description']
- self.assertEqual(desc2, project_desc, 'Description does not appear'
+ self.assertEqual(desc2, project_desc, 'Description does not appear '
'to be set')
@decorators.idempotent_id('5f50fe07-8166-430b-a882-3b2ee0abe26f')
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 2530072..83b3c30 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -39,7 +39,6 @@
# Use alt_username as the trustee
self.trust_id = None
self.create_trustor_and_roles()
- self.addCleanup(self.cleanup_user_and_roles)
def tearDown(self):
if self.trust_id:
@@ -55,6 +54,7 @@
trustor_project_name,
domain_id=CONF.identity.default_domain_id)['project']
self.trustor_project_id = project['id']
+ self.addCleanup(self.projects_client.delete_project, project['id'])
self.assertIsNotNone(self.trustor_project_id)
# Create a trustor User
@@ -69,6 +69,7 @@
email=u_email,
project_id=self.trustor_project_id,
domain_id=CONF.identity.default_domain_id)['user']
+ self.addCleanup(self.users_client.delete_user, user['id'])
self.trustor_user_id = user['id']
# And two roles, one we'll delegate and one we won't
@@ -76,10 +77,12 @@
self.not_delegated_role = data_utils.rand_name('NotDelegatedRole')
role = self.roles_client.create_role(name=self.delegated_role)['role']
+ self.addCleanup(self.roles_client.delete_role, role['id'])
self.delegated_role_id = role['id']
role = self.roles_client.create_role(
name=self.not_delegated_role)['role']
+ self.addCleanup(self.roles_client.delete_role, role['id'])
self.not_delegated_role_id = role['id']
# Assign roles to trustor
@@ -109,16 +112,6 @@
os = clients.Manager(credentials=creds)
self.trustor_client = os.trusts_client
- def cleanup_user_and_roles(self):
- if self.trustor_user_id:
- self.users_client.delete_user(self.trustor_user_id)
- if self.trustor_project_id:
- self.projects_client.delete_project(self.trustor_project_id)
- if self.delegated_role_id:
- self.roles_client.delete_role(self.delegated_role_id)
- if self.not_delegated_role_id:
- self.roles_client.delete_role(self.not_delegated_role_id)
-
def create_trust(self, impersonate=True, expires=None):
trust_create = self.trustor_client.create_trust(
diff --git a/tempest/api/identity/v2/test_ec2_credentials.py b/tempest/api/identity/v2/test_ec2_credentials.py
index 237e728..9981ef8 100644
--- a/tempest/api/identity/v2/test_ec2_credentials.py
+++ b/tempest/api/identity/v2/test_ec2_credentials.py
@@ -57,18 +57,19 @@
self.creds.user_id,
tenant_id=self.creds.tenant_id)["credential"]
created_creds.append(creds1['access'])
+ self.addCleanup(
+ self.non_admin_users_client.delete_user_ec2_credential,
+ self.creds.user_id, creds1['access'])
+
# create second ec2 credentials
creds2 = self.non_admin_users_client.create_user_ec2_credential(
self.creds.user_id,
tenant_id=self.creds.tenant_id)["credential"]
created_creds.append(creds2['access'])
- # add credentials to be cleaned up
- self.addCleanup(
- self.non_admin_users_client.delete_user_ec2_credential,
- self.creds.user_id, creds1['access'])
self.addCleanup(
self.non_admin_users_client.delete_user_ec2_credential,
self.creds.user_id, creds2['access'])
+
# get the list of user ec2 credentials
resp = self.non_admin_users_client.list_user_ec2_credentials(
self.creds.user_id)["credentials"]
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index 6d6baca..13b5161 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -133,6 +133,13 @@
'Security compliance not available.')
@decorators.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
def test_user_account_lockout(self):
+ if (CONF.identity.user_lockout_failure_attempts <= 0 or
+ CONF.identity.user_lockout_duration <= 0):
+ raise self.skipException(
+ "Both CONF.identity.user_lockout_failure_attempts and "
+ "CONF.identity.user_lockout_duration should be greater than "
+ "zero to test this feature")
+
password = self.creds.password
# First, we login using the correct credentials
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index e79f8c3..a075b51 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -14,7 +14,9 @@
# under the License.
from tempest.api.network import base
+from tempest.common import identity
from tempest.common import utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
@@ -30,7 +32,6 @@
quota_driver = neutron.db.quota_db.DbQuotaDriver
"""
- force_tenant_isolation = True
@classmethod
def skip_checks(cls):
@@ -39,27 +40,36 @@
msg = "quotas extension not enabled."
raise cls.skipException(msg)
+ def setUp(self):
+ super(QuotasNegativeTest, self).setUp()
+ name = data_utils.rand_name('test_project_')
+ description = data_utils.rand_name('desc_')
+ self.project = identity.identity_utils(self.os_admin).create_project(
+ name=name, description=description)
+ self.addCleanup(identity.identity_utils(self.os_admin).delete_project,
+ self.project['id'])
+
@decorators.attr(type=['negative'])
@decorators.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
def test_network_quota_exceeding(self):
# Set the network quota to two
- self.admin_quotas_client.update_quotas(self.networks_client.tenant_id,
- network=2)
- self.addCleanup(self.admin_quotas_client.reset_quotas,
- self.networks_client.tenant_id)
+ self.admin_quotas_client.update_quotas(self.project['id'], network=2)
# Create two networks
- n1 = self.networks_client.create_network()
- self.addCleanup(self.networks_client.delete_network,
+ n1 = self.admin_networks_client.create_network(
+ tenant_id=self.project['id'])
+ self.addCleanup(self.admin_networks_client.delete_network,
n1['network']['id'])
- n2 = self.networks_client.create_network()
- self.addCleanup(self.networks_client.delete_network,
+ n2 = self.admin_networks_client.create_network(
+ tenant_id=self.project['id'])
+ self.addCleanup(self.admin_networks_client.delete_network,
n2['network']['id'])
# Try to create a third network while the quota is two
with self.assertRaisesRegex(
lib_exc.Conflict,
r"Quota exceeded for resources: \['network'\].*"):
- n3 = self.networks_client.create_network()
- self.addCleanup(self.networks_client.delete_network,
+ n3 = self.admin_networks_client.create_network(
+ tenant_id=self.project['id'])
+ self.addCleanup(self.admin_networks_client.delete_network,
n3['network']['id'])
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index 0730d58..399954c 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -135,7 +135,7 @@
real_ip, eui_ip = self._get_ips_from_subnet(**kwargs)
self._clean_network()
self.assertEqual(eui_ip, real_ip,
- ('Real port IP %s shall be equal to EUI-64 %s'
+ ('Real port IP %s shall be equal to EUI-64 %s '
'when ipv6_ra_mode=%s,ipv6_address_mode=%s') % (
real_ip, eui_ip,
ra_mode if ra_mode else "Off",
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index 765bc6d..e9ca0b1 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -38,9 +38,9 @@
def test_read_object_with_rights(self):
# attempt to read object using authorized user
# update X-Container-Read metadata ACL
- tenant_name = self.os_roles_operator_alt.credentials.tenant_name
- username = self.os_roles_operator_alt.credentials.username
- cont_headers = {'X-Container-Read': tenant_name + ':' + username}
+ tenant_id = self.os_roles_operator_alt.credentials.tenant_id
+ user_id = self.os_roles_operator_alt.credentials.user_id
+ cont_headers = {'X-Container-Read': tenant_id + ':' + user_id}
container_client = self.os_roles_operator.container_client
resp_meta, _ = (
container_client.create_update_or_delete_container_metadata(
@@ -66,9 +66,9 @@
def test_write_object_with_rights(self):
# attempt to write object using authorized user
# update X-Container-Write metadata ACL
- tenant_name = self.os_roles_operator_alt.credentials.tenant_name
- username = self.os_roles_operator_alt.credentials.username
- cont_headers = {'X-Container-Write': tenant_name + ':' + username}
+ tenant_id = self.os_roles_operator_alt.credentials.tenant_id
+ user_id = self.os_roles_operator_alt.credentials.user_id
+ cont_headers = {'X-Container-Write': tenant_id + ':' + user_id}
container_client = self.os_roles_operator.container_client
resp_meta, _ = (
container_client.create_update_or_delete_container_metadata(
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index 29abd49..2f54f9a 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -279,7 +279,7 @@
self.admin_id,
self.admin_role_id)
except Exception as ex:
- LOG.exception("Failed removing role from project which still"
+ LOG.exception("Failed removing role from project which still "
"exists, exception: %s", ex)
def _project_exists(self, project_id):
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 84c6d9a..3e84b82 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -243,8 +243,8 @@
parser.add_argument('--load-list', '--load_list',
help='Path to a non-regex whitelist file, '
'this file contains a separate test '
- 'on each newline. This command'
- 'supports files created by the tempest'
+ 'on each newline. This command '
+ 'supports files created by the tempest '
'run ``--list-tests`` command')
# list only args
parser.add_argument('--list-tests', '-l', action='store_true',
diff --git a/tempest/cmd/workspace.py b/tempest/cmd/workspace.py
index d276bde..d0c4b28 100644
--- a/tempest/cmd/workspace.py
+++ b/tempest/cmd/workspace.py
@@ -94,7 +94,7 @@
@lockutils.synchronized('workspaces', external=True)
def move_workspace(self, name, path):
self._populate()
- path = os.path.abspath(os.path.expanduser(path))
+ path = os.path.abspath(os.path.expanduser(path)) if path else path
self._name_exists(name)
self._validate_path(path)
self.workspaces[name] = path
@@ -115,6 +115,7 @@
@lockutils.synchronized('workspaces', external=True)
def remove_workspace_directory(self, workspace_path):
+ self._validate_path(workspace_path)
shutil.rmtree(workspace_path)
@lockutils.synchronized('workspaces', external=True)
@@ -136,6 +137,10 @@
sys.exit(1)
def _validate_path(self, path):
+ if not path:
+ print("None or empty path is specified for workspace."
+ " Please specify correct workspace path.")
+ sys.exit(1)
if not os.path.exists(path):
print("Path does not exist.")
sys.exit(1)
@@ -144,7 +149,7 @@
def register_new_workspace(self, name, path, init=False):
"""Adds the new workspace and writes out the new workspace config"""
self._populate()
- path = os.path.abspath(os.path.expanduser(path))
+ path = os.path.abspath(os.path.expanduser(path)) if path else path
# This only happens when register is called from outside of init
if not init:
self._validate_path(path)
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index f2730b3..e218d5a 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -44,15 +44,14 @@
def is_scheduler_filter_enabled(filter_name):
"""Check the list of enabled compute scheduler filters from config.
- This function checks whether the given compute scheduler filter is
- available and configured in the config file. If the
- scheduler_available_filters option is set to 'all' (Default value. which
- means default filters are configured in nova) in tempest.conf then, this
- function returns True with assumption that requested filter 'filter_name'
- is one of available filter in nova ("nova.scheduler.filters.all_filters").
+ This function checks whether the given compute scheduler filter is enabled
+ in the nova config file. If the scheduler_enabled_filters option is set to
+ 'all' in tempest.conf then, this function returns True with assumption that
+ requested filter 'filter_name' is one of the enabled filters in nova
+ ("nova.scheduler.filters.all_filters").
"""
- filters = CONF.compute_feature_enabled.scheduler_available_filters
+ filters = CONF.compute_feature_enabled.scheduler_enabled_filters
if not filters:
return False
if 'all' in filters:
diff --git a/tempest/config.py b/tempest/config.py
index dbce504..18c9d9d 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -170,15 +170,35 @@
cfg.IntOpt('user_lockout_failure_attempts',
default=2,
help="The number of unsuccessful login attempts the user is "
- "allowed before having the account locked."),
+ "allowed before having the account locked. This only "
+ "takes effect when identity-feature-enabled."
+ "security_compliance is set to 'True'. For more details, "
+ "refer to keystone config options keystone.conf:"
+ "security_compliance.lockout_failure_attempts. "
+ "This feature is disabled by default in keystone."),
cfg.IntOpt('user_lockout_duration',
default=5,
help="The number of seconds a user account will remain "
- "locked."),
+ "locked. This only takes "
+ "effect when identity-feature-enabled.security_compliance "
+ "is set to 'True'. For more details, refer to "
+ "keystone config options "
+ "keystone.conf:security_compliance.lockout_duration. "
+ "Setting this option will have no effect unless you also "
+ "set identity.user_lockout_failure_attempts."),
cfg.IntOpt('user_unique_last_password_count',
default=2,
help="The number of passwords for a user that must be unique "
- "before an old password can be reused."),
+ "before an old password can be reused. This only takes "
+ "effect when identity-feature-enabled.security_compliance "
+ "is set to 'True'."
+ "This config option corresponds to keystone.conf: "
+ "security_compliance.unique_last_password_count, whose "
+ "default value is 0 meaning disabling this feature. "
+ "NOTE: This config option value must be same as "
+ "keystone.conf: security_compliance.unique_last_password_"
+ "count otherwise test might fail"
+ ),
]
service_clients_group = cfg.OptGroup(name='service-clients',
@@ -463,20 +483,30 @@
cfg.BoolOpt('config_drive',
default=True,
help='Enable special configuration drive with metadata.'),
- cfg.ListOpt('scheduler_available_filters',
- default=['all'],
- help="A list of enabled filters that nova will accept as hints"
- " to the scheduler when creating a server. A special "
- "entry 'all' indicates all filters that are included "
- "with nova are enabled. Empty list indicates all filters "
- "are disabled. The full list of available filters is in "
- "nova.conf: filter_scheduler.enabled_filters. If the "
+ cfg.ListOpt('scheduler_enabled_filters',
+ default=["RetryFilter", "AvailabilityZoneFilter",
+ "ComputeFilter", "ComputeCapabilitiesFilter",
+ "ImagePropertiesFilter",
+ "ServerGroupAntiAffinityFilter",
+ "ServerGroupAffinityFilter"],
+ help="A list of enabled filters that Nova will accept as "
+ "hints to the scheduler when creating a server. If the "
"default value is overridden in nova.conf by the test "
"environment (which means that a different set of "
"filters is enabled than what is included in Nova by "
- "default) then, this option must be configured to "
+ "default), then this option must be configured to "
"contain the same filters that Nova uses in the test "
- "environment."),
+ "environment. A special entry 'all' indicates all "
+ "filters that are included with Nova are enabled. If "
+ "using 'all', be sure to enable all filters in "
+ "nova.conf, as tests can fail in unpredictable ways if "
+ "Nova's and Tempest's enabled filters don't match. "
+ "Empty list indicates all filters are disabled. The "
+ "full list of enabled filters is in nova.conf: "
+ "filter_scheduler.enabled_filters.",
+ deprecated_opts=[cfg.DeprecatedOpt(
+ 'scheduler_available_filters',
+ group='compute-feature-enabled')]),
cfg.BoolOpt('swap_volume',
default=False,
help='Does the test environment support in-place swapping of '
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 2dd9d00..8e6d3d5 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -324,7 +324,7 @@
pass
if expiry is None:
raise ValueError(
- "time data '{data}' does not match any of the"
+ "time data '{data}' does not match any of the "
"expected formats: {formats}".format(
data=expiry_string, formats=self.EXPIRY_DATE_FORMATS))
return expiry
diff --git a/tempest/lib/common/jsonschema_validator.py b/tempest/lib/common/jsonschema_validator.py
index 9a35b76..bbf5e89 100644
--- a/tempest/lib/common/jsonschema_validator.py
+++ b/tempest/lib/common/jsonschema_validator.py
@@ -12,9 +12,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-import base64
-
import jsonschema
+from oslo_serialization import base64
from oslo_utils import timeutils
import six
@@ -46,9 +45,7 @@
try:
if isinstance(instance, six.text_type):
instance = instance.encode('utf-8')
- base64.decodestring(instance)
- except base64.binascii.Error:
- return False
+ base64.decode_as_bytes(instance)
except TypeError:
# The name must be string type. If instance isn't string type, the
# TypeError will be raised at here.
diff --git a/tempest/lib/common/utils/misc.py b/tempest/lib/common/utils/misc.py
index f13b4c8..2b0fcd5 100644
--- a/tempest/lib/common/utils/misc.py
+++ b/tempest/lib/common/utils/misc.py
@@ -14,8 +14,6 @@
# under the License.
from oslo_log import log as logging
-from tempest.lib.common.utils import test_utils
-
LOG = logging.getLogger(__name__)
@@ -28,10 +26,3 @@
instances[cls] = cls()
return instances[cls]
return getinstance
-
-
-def find_test_caller(*args, **kwargs):
- LOG.warning("tempest.lib.common.utils.misc.find_test_caller is deprecated "
- "in favor of tempest.lib.common.utils.test_utils."
- "find_test_caller")
- test_utils.find_test_caller(*args, **kwargs)
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index f2d2d21..fb64333 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -104,7 +104,12 @@
return rest_client.ResponseBody(resp, body)
def import_backup(self, **kwargs):
- """Import backup metadata record."""
+ """Import backup metadata record.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/index.html#import-a-backup
+ """
post_body = json.dumps({'backup-record': kwargs})
resp, body = self.post("backups/import_record", post_body)
body = json.loads(body)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 57a560c..438ee01 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -280,6 +280,7 @@
dualnet=True)
@decorators.idempotent_id('9178ad42-10e4-47e9-8987-e02b170cc5cd')
+ @decorators.attr(type='slow')
@utils.services('compute', 'network')
def test_dualnet_multi_prefix_slaac(self):
self._prepare_and_test(address6_mode='slaac', n_subnets6=2,
diff --git a/tempest/test.py b/tempest/test.py
index f2babbb..3e98c33 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -581,7 +581,7 @@
def setUp(self):
super(BaseTestCase, self).setUp()
if not self.__setupclass_called:
- raise RuntimeError("setUpClass does not calls the super's"
+ raise RuntimeError("setUpClass does not calls the super's "
"setUpClass in the " +
self.__class__.__name__)
at_exit_set.add(self.__class__)
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index a1d3a40..b349bba 100644
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -106,6 +106,8 @@
cp = account_generator.get_credential_provider(self.opts)
admin_creds = cp.default_admin_creds
self.assertEqual(self.opts.os_tenant_name, admin_creds.tenant_name)
+ self.assertEqual(self.opts.os_username, admin_creds.username)
+ self.assertEqual(self.opts.os_password, admin_creds.password)
class TestAccountGeneratorV3(TestAccountGeneratorV2):
@@ -222,6 +224,30 @@
self.assertIsNotNone(resource[1].router)
self.assertIsNotNone(resource[1].subnet)
+ def test_generate_resources_swift_no_admin(self):
+ cfg.CONF.set_default('swift', True, group='service_available')
+ cfg.CONF.set_default('operator_role', 'fake_operator',
+ group='object-storage')
+ cfg.CONF.set_default('reseller_admin_role', 'fake_reseller',
+ group='object-storage')
+ resources = account_generator.generate_resources(
+ self.cred_provider, admin=False)
+ resource_types = [k for k, _ in resources]
+ # No Admin, swift, expect four credentials only
+ self.assertEqual(4, len(resources))
+ # Ensure create_user was invoked 4 times (4 distinct users)
+ self.assertEqual(4, self.user_create_fixture.mock.call_count)
+ self.assertIn('primary', resource_types)
+ self.assertIn('alt', resource_types)
+ self.assertNotIn('admin', resource_types)
+ self.assertIn(['fake_operator'], resource_types)
+ self.assertIn(['fake_reseller'], resource_types)
+ self.assertNotIn(['fake_owner'], resource_types)
+ for resource in resources:
+ self.assertIsNotNone(resource[1].network)
+ self.assertIsNotNone(resource[1].router)
+ self.assertIsNotNone(resource[1].subnet)
+
class TestGenerateResourcesV3(TestGenerateResourcesV2):
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 9a6be4e..e159cdc 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -24,7 +24,9 @@
import six
from tempest.cmd import run
+from tempest.cmd import workspace
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.tests import base
DEVNULL = open(os.devnull, 'wb')
@@ -58,6 +60,12 @@
self.assertEqual(['i_am_a_fun_little_regex'],
self.run_cmd._build_regex(args))
+ def test__build_regex_smoke_regex(self):
+ args = mock.Mock(spec=argparse.Namespace)
+ setattr(args, "smoke", True)
+ setattr(args, 'regex', 'i_am_a_fun_little_regex')
+ self.assertEqual(['smoke'], self.run_cmd._build_regex(args))
+
class TestRunReturnCode(base.TestCase):
def setUp(self):
@@ -173,6 +181,27 @@
class TestTakeAction(base.TestCase):
+ def setUp(self):
+ super(TestTakeAction, self).setUp()
+ self.name = data_utils.rand_name('workspace')
+ self.path = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.path, ignore_errors=True)
+ store_dir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, store_dir, ignore_errors=True)
+ self.store_file = os.path.join(store_dir, 'workspace.yaml')
+ self.workspace_manager = workspace.WorkspaceManager(
+ path=self.store_file)
+ self.workspace_manager.register_new_workspace(self.name, self.path)
+
+ def _setup_test_dirs(self):
+ self.directory = tempfile.mkdtemp(prefix='tempest-unit')
+ self.addCleanup(shutil.rmtree, self.directory, ignore_errors=True)
+ self.test_dir = os.path.join(self.directory, 'tests')
+ os.mkdir(self.test_dir)
+ # Change directory, run wrapper and check result
+ self.addCleanup(os.chdir, os.path.abspath(os.curdir))
+ os.chdir(self.directory)
+
def test_workspace_not_registered(self):
class Exception_(Exception):
pass
@@ -201,15 +230,7 @@
self.assertIn(workspace, exit_msg)
def test_config_file_specified(self):
- # Setup test dirs
- self.directory = tempfile.mkdtemp(prefix='tempest-unit')
- self.addCleanup(shutil.rmtree, self.directory)
- self.test_dir = os.path.join(self.directory, 'tests')
- os.mkdir(self.test_dir)
- # Change directory, run wrapper and check result
- self.addCleanup(os.chdir, os.path.abspath(os.curdir))
- os.chdir(self.directory)
-
+ self._setup_test_dirs()
tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
parsed_args = mock.Mock()
@@ -222,3 +243,97 @@
m.return_value = 0
self.assertEqual(0, tempest_run.take_action(parsed_args))
m.assert_called()
+
+ def test_no_config_file_no_workspace_no_state(self):
+ self._setup_test_dirs()
+ tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+ parsed_args = mock.Mock()
+
+ parsed_args.workspace = None
+ parsed_args.state = None
+ parsed_args.list_tests = False
+ parsed_args.config_file = ''
+
+ with mock.patch('stestr.commands.run_command'):
+ self.assertRaises(SystemExit, tempest_run.take_action, parsed_args)
+
+ def test_config_file_workspace_registered(self):
+ self._setup_test_dirs()
+ tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+ parsed_args = mock.Mock()
+ parsed_args.workspace = self.name
+ parsed_args.workspace_path = self.store_file
+ parsed_args.state = None
+ parsed_args.list_tests = False
+ parsed_args.config_file = '.stestr.conf'
+
+ with mock.patch('stestr.commands.run_command') as m:
+ m.return_value = 0
+ self.assertEqual(0, tempest_run.take_action(parsed_args))
+ m.assert_called()
+
+ @mock.patch('tempest.cmd.run.TempestRun._init_state')
+ def test_workspace_registered_no_config_no_state(self, mock_init_state):
+ self._setup_test_dirs()
+ tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+ parsed_args = mock.Mock()
+ parsed_args.workspace = self.name
+ parsed_args.workspace_path = self.store_file
+ parsed_args.state = None
+ parsed_args.list_tests = False
+ parsed_args.config_file = ''
+
+ with mock.patch('stestr.commands.run_command') as m:
+ m.return_value = 0
+ self.assertEqual(0, tempest_run.take_action(parsed_args))
+ m.assert_called()
+ mock_init_state.assert_not_called()
+
+ @mock.patch('tempest.cmd.run.TempestRun._init_state')
+ def test_no_config_file_no_workspace_state_true(self, mock_init_state):
+ self._setup_test_dirs()
+ tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+ parsed_args = mock.Mock()
+
+ parsed_args.workspace = None
+ parsed_args.state = True
+ parsed_args.list_tests = False
+ parsed_args.config_file = ''
+
+ with mock.patch('stestr.commands.run_command'):
+ self.assertRaises(SystemExit, tempest_run.take_action, parsed_args)
+ mock_init_state.assert_not_called()
+
+ @mock.patch('tempest.cmd.run.TempestRun._init_state')
+ def test_workspace_registered_no_config_state_true(self, mock_init_state):
+ self._setup_test_dirs()
+ tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+ parsed_args = mock.Mock()
+ parsed_args.workspace = self.name
+ parsed_args.workspace_path = self.store_file
+ parsed_args.state = True
+ parsed_args.list_tests = False
+ parsed_args.config_file = ''
+
+ with mock.patch('stestr.commands.run_command') as m:
+ m.return_value = 0
+ self.assertEqual(0, tempest_run.take_action(parsed_args))
+ m.assert_called()
+ mock_init_state.assert_called()
+
+ @mock.patch('tempest.cmd.run.TempestRun._init_state')
+ def test_no_workspace_config_file_state_true(self, mock_init_state):
+ self._setup_test_dirs()
+ tempest_run = run.TempestRun(app=mock.Mock(), app_args=mock.Mock())
+ parsed_args = mock.Mock()
+ parsed_args.workspace = None
+ parsed_args.workspace_path = self.store_file
+ parsed_args.state = True
+ parsed_args.list_tests = False
+ parsed_args.config_file = '.stestr.conf'
+
+ with mock.patch('stestr.commands.run_command') as m:
+ m.return_value = 0
+ self.assertEqual(0, tempest_run.take_action(parsed_args))
+ m.assert_called()
+ mock_init_state.assert_called()
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index a256368..65481de 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -140,6 +140,17 @@
self.assertEqual(
self.workspace_manager.get_workspace(self.name), new_path)
+ def test_workspace_manager_move_no_workspace_path(self):
+ new_path = ""
+ with patch('sys.stdout', new_callable=StringIO) as mock_stdout:
+ ex = self.assertRaises(SystemExit,
+ self.workspace_manager.move_workspace,
+ self.name, new_path)
+ self.assertEqual(1, ex.code)
+ self.assertEqual(mock_stdout.getvalue(),
+ "None or empty path is specified for workspace."
+ " Please specify correct workspace path.\n")
+
def test_workspace_manager_remove_entry(self):
self.workspace_manager.remove_workspace_entry(self.name)
self.assertIsNone(self.workspace_manager.get_workspace(self.name))
@@ -149,6 +160,18 @@
self.workspace_manager.remove_workspace_directory(path)
self.assertIsNone(self.workspace_manager.get_workspace(self.name))
+ def test_workspace_manager_remove_directory_no_path(self):
+ no_path = ""
+ with patch('sys.stdout', new_callable=StringIO) as mock_stdout:
+ ex = self.assertRaises(SystemExit,
+ self.workspace_manager.
+ remove_workspace_directory,
+ no_path)
+ self.assertEqual(1, ex.code)
+ self.assertEqual(mock_stdout.getvalue(),
+ "None or empty path is specified for workspace."
+ " Please specify correct workspace path.\n")
+
def test_path_expansion(self):
name = data_utils.rand_uuid()
path = os.path.join("~", name)
@@ -207,3 +230,15 @@
self.assertEqual(mock_stdout.getvalue(),
"None or empty name is specified."
" Please specify correct name for workspace.\n")
+
+ def test_register_new_workspace_no_path(self):
+ no_path = ""
+ with patch('sys.stdout', new_callable=StringIO) as mock_stdout:
+ ex = self.assertRaises(SystemExit,
+ self.workspace_manager.
+ register_new_workspace,
+ self.name, no_path)
+ self.assertEqual(1, ex.code)
+ self.assertEqual(mock_stdout.getvalue(),
+ "None or empty path is specified for workspace."
+ " Please specify correct workspace path.\n")
diff --git a/tempest/tests/lib/services/image/v2/test_resource_types_client.py b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
index 2e3b117..741b4eb 100644
--- a/tempest/tests/lib/services/image/v2/test_resource_types_client.py
+++ b/tempest/tests/lib/services/image/v2/test_resource_types_client.py
@@ -17,7 +17,7 @@
from tempest.tests.lib.services import base
-class TestResouceTypesClient(base.BaseServiceTest):
+class TestResourceTypesClient(base.BaseServiceTest):
FAKE_LIST_RESOURCETYPES = {
"resource_types": [
{
@@ -49,21 +49,21 @@
}
def setUp(self):
- super(TestResouceTypesClient, self).setUp()
+ super(TestResourceTypesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
self.client = resource_types_client.ResourceTypesClient(fake_auth,
'image',
'regionOne')
- def _test_list_resouce_types(self, bytes_body=False):
+ def _test_list_resource_types(self, bytes_body=False):
self.check_service_client_function(
self.client.list_resource_types,
'tempest.lib.common.rest_client.RestClient.get',
self.FAKE_LIST_RESOURCETYPES,
bytes_body)
- def test_list_resouce_types_with_str_body(self):
- self._test_list_resouce_types()
+ def test_list_resource_types_with_str_body(self):
+ self._test_list_resource_types()
- def test_list_resouce_types_with_bytes_body(self):
- self._test_list_resouce_types(bytes_body=True)
+ def test_list_resource_types_with_bytes_body(self):
+ self._test_list_resource_types(bytes_body=True)
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index 4eb78fb..3772774 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -74,7 +74,8 @@
# Gerrit prepends 4 garbage octets to the JSON, in order to counter
# cross-site scripting attacks. Therefore we must discard it so the
# json library won't choke.
-projects = sorted(filter(is_in_openstack_namespace, json.loads(r.read()[4:])))
+content = r.read().decode('utf-8')[4:]
+projects = sorted(filter(is_in_openstack_namespace, json.loads(content)))
# Retrieve projects having no deb, puppet, ui or spec namespace as those
# namespaces do not contains tempest plugins.
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index b27b23a..111c9ce 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -41,8 +41,6 @@
set -ex
(
-declare -A plugins
-
if [[ -r doc/source/data/tempest-plugins-registry.header ]]; then
cat doc/source/data/tempest-plugins-registry.header
fi
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index 8b4f913..661329b 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -47,7 +47,7 @@
# retrieve a list of projects having tempest plugins
PROJECT_LIST="$(python tools/generate-tempest-plugins-list.py)"
# List of projects having tempest plugin stale or unmaintained from long time
-BLACKLIST="trio2o"
+BLACKLIST="networking-plumgrid,trio2o"
# Function to clone project using zuul-cloner or from git
function clone_project() {
@@ -105,8 +105,8 @@
# Function to run sanity check on each project
function plugin_sanity_check() {
- clone_project "$1" && install_project "$1" && tempest_sanity "$1" \
- && uninstall_project "$1" && "$TVENV" pip install .
+ clone_project "$1" && install_project "$1" && tempest_sanity "$1" \
+ && uninstall_project "$1" && "$TVENV" pip install .
}
# Log status
diff --git a/tox.ini b/tox.ini
index 8208066..4d3c622 100644
--- a/tox.ini
+++ b/tox.ini
@@ -20,7 +20,7 @@
OS_STDERR_CAPTURE=1
OS_TEST_TIMEOUT=160
PYTHONWARNINGS=default::DeprecationWarning,ignore::DeprecationWarning:distutils,ignore::DeprecationWarning:site
-passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
+passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST GABBI_TEMPEST_PATH
usedevelop = True
install_command = pip install {opts} {packages}
whitelist_externals = *
@@ -182,6 +182,7 @@
commands = {posargs}
[testenv:docs]
+basepython = python3
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
-r{toxinidir}/requirements.txt
@@ -192,6 +193,7 @@
whitelist_externals = rm
[testenv:pep8]
+basepython = python3
commands =
flake8 {posargs}
check-uuid
@@ -216,6 +218,7 @@
import-order-style = pep8
[testenv:releasenotes]
+basepython = python3
deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
-r{toxinidir}/requirements.txt