Merge "Correct test_list_servers_filtered_by_ip for bug 1668828""
diff --git a/README.rst b/README.rst
index 9d19c23..3d7c804 100644
--- a/README.rst
+++ b/README.rst
@@ -209,15 +209,9 @@
Python 3.x
----------
-Starting during the Liberty release development cycle work began on enabling
-Tempest to run under both Python 2.7 and Python 3.4. Tempest strives to fully
-support running with Python 3.4 and newer. A gating unit test job was added to
-also run Tempest's unit tests under Python 3. This means that the Tempest
-code at least imports under Python 3.4 and things that have unit test coverage
-will work on Python 3.4. However, because large parts of Tempest are
-self-verifying there might be uncaught issues running on Python 3. So until
-there is a gating job which does a full Tempest run using Python 3 there
-isn't any guarantee that running Tempest under Python 3 is bug free.
+Starting during the Pike cycle Tempest has a gating CI job that runs tempest
+with Python 3. Any tempest release after 15.0.0 should fully support running
+under Python 3 as well as Python 2.7.
Legacy run method
-----------------
diff --git a/doc/source/test-removal.rst b/doc/source/test-removal.rst
index 4757dc4..d06e4ba 100644
--- a/doc/source/test-removal.rst
+++ b/doc/source/test-removal.rst
@@ -64,7 +64,7 @@
SELECT * from tests where test_id like "%test_id%";
(where $test_id is the full test_id, but truncated to the class because of
-setupClass or tearDownClass failures)
+setUpClass or tearDownClass failures)
You can access the infra mysql subunit2sql db w/ read-only permissions with:
@@ -82,7 +82,7 @@
#. run the query: MySQL [subunit2sql]> select * from tests where test_id like
"tempest.api.compute.admin.test_flavors_negative.FlavorsAdminNegativeTestJSON%";
which will return a table of all the tests in the class (but it will also
- catch failures in setupClass and tearDownClass)
+ catch failures in setUpClass and tearDownClass)
#. paste the output table with numbers and the mysql command you ran to
generate it into the etherpad.
@@ -185,4 +185,4 @@
If a test exists in Tempest that would meet this criteria as consequence of a
change, the test must be removed according to the procedure discussed into
this document. The API change should not be merged until all conditions
-required for test removal can be met.
\ No newline at end of file
+required for test removal can be met.
diff --git a/releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml
new file mode 100644
index 0000000..73900ca
--- /dev/null
+++ b/releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Adds a new cli option to tempest run, --combine, which is used to indicate
+ you want the subunit stream output combined with the previous run's in
+ the testr repository
diff --git a/releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml
new file mode 100644
index 0000000..9927971
--- /dev/null
+++ b/releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Add server tags APIs to the servers_client library.
+ This feature enables the possibility of upating, deleting
+ and checking existence of a tag on a server, as well
+ as updating and deleting all tags on a server.
+
diff --git a/releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml b/releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml
new file mode 100644
index 0000000..dd6e924
--- /dev/null
+++ b/releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - Tempest now defaults to using Keystone v3 API for the
+ authentication, because Keystone v3 API is CURRENT and
+ the v2 API is deprecated.
diff --git a/requirements.txt b/requirements.txt
index d9a9ebb..124da7a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,7 +12,7 @@
oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
oslo.log>=3.11.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
-oslo.utils>=3.18.0 # Apache-2.0
+oslo.utils>=3.20.0 # Apache-2.0
six>=1.9.0 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD
PyYAML>=3.10.0 # MIT
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index a9daba8..5a38acc 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -37,7 +37,6 @@
# Non admin tenant ID
cls.tenant_id = cls.flavors_client.tenant_id
- cls.flavor_name_prefix = 'test_flavor_access_'
cls.ram = 512
cls.vcpus = 1
cls.disk = 10
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 33d5d73..12e4587 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -39,7 +39,6 @@
super(FlavorsAccessNegativeTestJSON, cls).resource_setup()
cls.tenant_id = cls.flavors_client.tenant_id
- cls.flavor_name_prefix = 'test_flavor_access_'
cls.ram = 512
cls.vcpus = 1
cls.disk = 10
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index aa75348..18655cb 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -30,7 +30,6 @@
def setup_clients(cls):
super(MigrationsAdminTest, cls).setup_clients()
cls.client = cls.os_adm.migrations_client
- cls.flavors_admin_client = cls.os_adm.flavors_client
@decorators.idempotent_id('75c0b83d-72a0-4cf8-a153-631e83e7d53f')
def test_list_migrations(self):
@@ -54,8 +53,8 @@
def _flavor_clean_up(self, flavor_id):
try:
- self.flavors_admin_client.delete_flavor(flavor_id)
- self.flavors_admin_client.wait_for_resource_deletion(flavor_id)
+ self.admin_flavors_client.delete_flavor(flavor_id)
+ self.admin_flavors_client.wait_for_resource_deletion(flavor_id)
except exceptions.NotFound:
pass
@@ -68,9 +67,9 @@
# First we have to create a flavor that we can delete so make a copy
# of the normal flavor from which we'd create a server.
- flavor = self.flavors_admin_client.show_flavor(
+ flavor = self.admin_flavors_client.show_flavor(
self.flavor_ref)['flavor']
- flavor = self.flavors_admin_client.create_flavor(
+ flavor = self.admin_flavors_client.create_flavor(
name=data_utils.rand_name('test_resize_flavor_'),
ram=flavor['ram'],
disk=flavor['disk'],
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 13a411b..adb49a5 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -34,7 +34,6 @@
super(ServersAdminNegativeTestJSON, cls).setup_clients()
cls.client = cls.os_adm.servers_client
cls.non_adm_client = cls.servers_client
- cls.flavors_client = cls.os_adm.flavors_client
cls.quotas_client = cls.os_adm.quotas_client
@classmethod
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 20646d1..fd5e50e 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -236,7 +236,6 @@
@classmethod
def setup_clients(cls):
super(ServersWithSpecificFlavorTestJSON, cls).setup_clients()
- cls.flavor_client = cls.os_adm.flavors_client
cls.client = cls.servers_client
@classmethod
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 6160024..b915739 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -471,7 +471,7 @@
# NOTE: SHUTOFF is irregular status. To avoid test instability,
# one server is created only for this test without using
- # the server that was created in setupClass.
+ # the server that was created in setUpClass.
server = self.create_test_server(wait_until='ACTIVE')
temp_server_id = server['id']
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index dfda51b..cf4ed85 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -49,7 +49,7 @@
# We do not know the exact network configuration, but an instance
# should at least have a single public or private address
self.assertGreaterEqual(len(addresses), 1)
- for network_name, network_addresses in addresses.items():
+ for network_addresses in addresses.values():
self.assertGreaterEqual(len(network_addresses), 1)
for address in network_addresses:
self.assertTrue(address['addr'])
diff --git a/tempest/api/compute/servers/test_server_tags.py b/tempest/api/compute/servers/test_server_tags.py
new file mode 100644
index 0000000..20e2cee
--- /dev/null
+++ b/tempest/api/compute/servers/test_server_tags.py
@@ -0,0 +1,108 @@
+# Copyright 2017 AT&T Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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 six
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.lib import decorators
+from tempest import test
+
+
+class ServerTagsTestJSON(base.BaseV2ComputeTest):
+
+ min_microversion = '2.26'
+ max_microversion = 'latest'
+
+ @classmethod
+ def skip_checks(cls):
+ super(ServerTagsTestJSON, cls).skip_checks()
+ if not test.is_extension_enabled('os-server-tags', 'compute'):
+ msg = "os-server-tags extension is not enabled."
+ raise cls.skipException(msg)
+
+ @classmethod
+ def setup_clients(cls):
+ super(ServerTagsTestJSON, cls).setup_clients()
+ cls.client = cls.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ super(ServerTagsTestJSON, cls).resource_setup()
+ cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+ def _update_server_tags(self, server_id, tags):
+ if not isinstance(tags, (list, tuple)):
+ tags = [tags]
+ for tag in tags:
+ self.client.update_tag(server_id, tag)
+ self.addCleanup(self.client.delete_all_tags, server_id)
+
+ @decorators.idempotent_id('8d95abe2-c658-4c42-9a44-c0258500306b')
+ def test_create_delete_tag(self):
+ # Check that no tags exist.
+ fetched_tags = self.client.list_tags(self.server['id'])['tags']
+ self.assertEmpty(fetched_tags)
+
+ # Add server tag to the server.
+ assigned_tag = data_utils.rand_name('tag')
+ self._update_server_tags(self.server['id'], assigned_tag)
+
+ # Check that added tag exists.
+ fetched_tags = self.client.list_tags(self.server['id'])['tags']
+ self.assertEqual([assigned_tag], fetched_tags)
+
+ # Remove assigned tag from server and check that it was removed.
+ self.client.delete_tag(self.server['id'], assigned_tag)
+ fetched_tags = self.client.list_tags(self.server['id'])['tags']
+ self.assertEmpty(fetched_tags)
+
+ @decorators.idempotent_id('a2c1af8c-127d-417d-974b-8115f7e3d831')
+ def test_update_all_tags(self):
+ # Add server tags to the server.
+ tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
+ self._update_server_tags(self.server['id'], tags)
+
+ # Replace tags with new tags and check that they are present.
+ new_tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
+ replaced_tags = self.client.update_all_tags(
+ self.server['id'], new_tags)['tags']
+ six.assertCountEqual(self, new_tags, replaced_tags)
+
+ # List the tags and check that the tags were replaced.
+ fetched_tags = self.client.list_tags(self.server['id'])['tags']
+ six.assertCountEqual(self, new_tags, fetched_tags)
+
+ @decorators.idempotent_id('a63b2a74-e918-4b7c-bcab-10c855f3a57e')
+ def test_delete_all_tags(self):
+ # Add server tags to the server.
+ assigned_tags = [data_utils.rand_name('tag'),
+ data_utils.rand_name('tag')]
+ self._update_server_tags(self.server['id'], assigned_tags)
+
+ # Delete tags from the server and check that they were deleted.
+ self.client.delete_all_tags(self.server['id'])
+ fetched_tags = self.client.list_tags(self.server['id'])['tags']
+ self.assertEmpty(fetched_tags)
+
+ @decorators.idempotent_id('81279a66-61c3-4759-b830-a2dbe64cbe08')
+ def test_check_tag_existence(self):
+ # Add server tag to the server.
+ assigned_tag = data_utils.rand_name('tag')
+ self._update_server_tags(self.server['id'], assigned_tag)
+
+ # Check that added tag exists. Throws a 404 if not found, else a 204,
+ # which was already checked by the schema validation.
+ self.client.check_tag_existence(self.server['id'], assigned_tag)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index b22a434..1418b3f 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -176,7 +176,7 @@
self.assertRaises(lib_exc.NotFound,
self.client.rebuild_server,
- server['id'], self.image_ref_alt)
+ server['id'], self.image_ref)
@test.related_bug('1660878', status_code=409)
@test.attr(type=['negative'])
@@ -198,7 +198,7 @@
self.assertRaises(lib_exc.NotFound,
self.client.rebuild_server,
nonexistent_server,
- self.image_ref_alt)
+ self.image_ref)
@test.attr(type=['negative'])
@decorators.idempotent_id('fd57f159-68d6-4c2a-902b-03070828a87e')
diff --git a/tempest/api/compute/test_versions.py b/tempest/api/compute/test_versions.py
index c9f0724..dcab067 100644
--- a/tempest/api/compute/test_versions.py
+++ b/tempest/api/compute/test_versions.py
@@ -14,11 +14,13 @@
from tempest.api.compute import base
from tempest.lib import decorators
+from tempest import test
class TestVersions(base.BaseV2ComputeTest):
@decorators.idempotent_id('6c0a0990-43b6-4529-9b61-5fd8daf7c55c')
+ @test.attr(type='smoke')
def test_list_api_versions(self):
"""Test that a get of the unversioned url returns the choices doc.
@@ -37,6 +39,7 @@
"The first listed version should be v2.0")
@decorators.idempotent_id('b953a29e-929c-4a8e-81be-ec3a7e03cb76')
+ @test.attr(type='smoke')
def test_get_version_details(self):
"""Test individual version endpoints info works.
diff --git a/tempest/api/identity/admin/v2/test_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
index df55d2f..0ea2eb3 100644
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ b/tempest/api/identity/admin/v2/test_endpoints.py
@@ -27,10 +27,10 @@
s_name = data_utils.rand_name('service')
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
- cls.service_data = cls.services_client.create_service(
+ service_data = cls.services_client.create_service(
name=s_name, type=s_type,
description=s_description)['OS-KSADM:service']
- cls.service_id = cls.service_data['id']
+ cls.service_id = service_data['id']
cls.service_ids.append(cls.service_id)
# Create endpoints so as to use for LIST and GET test cases
cls.setup_endpoints = list()
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 686743b..9a0b3e4 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -33,11 +33,10 @@
s_name = data_utils.rand_name('service')
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
- cls.service_data = (
+ service_data = (
cls.services_client.create_service(name=s_name, type=s_type,
description=s_description))
- cls.service_data = cls.service_data['service']
- cls.service_id = cls.service_data['id']
+ cls.service_id = service_data['service']['id']
cls.service_ids.append(cls.service_id)
# Create endpoints so as to use for LIST and GET test cases
cls.setup_endpoints = list()
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index 53c2b1f..8e00193 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -35,11 +35,11 @@
s_name = data_utils.rand_name('service')
s_type = data_utils.rand_name('type')
s_description = data_utils.rand_name('description')
- cls.service_data = (
+ service_data = (
cls.services_client.create_service(name=s_name, type=s_type,
description=s_description)
['service'])
- cls.service_id = cls.service_data['id']
+ cls.service_id = service_data['id']
cls.service_ids.append(cls.service_id)
@classmethod
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index b7b6596..9bee24a 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -15,11 +15,14 @@
from tempest.api.identity import base
from tempest.common.utils import data_utils
+from tempest import config
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
from tempest import test
+CONF = config.CONF
+
class RolesV3TestJSON(base.BaseIdentityV3AdminTest):
@@ -348,6 +351,15 @@
# domain role to a global one
self._create_implied_role(domain_role1['id'], self.role['id'])
+ if CONF.identity_feature_enabled.forbid_global_implied_dsr:
+ # The contrary is not true: we can't create an inference rule
+ # from a global role to a domain role
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.roles_client.create_role_inference_rule,
+ self.role['id'],
+ domain_role1['id'])
+
@decorators.idempotent_id('3859df7e-5b78-4e4d-b10e-214c8953842a')
def test_assignments_for_domain_roles(self):
domain_role = self.setup_test_role(domain_id=self.domain['id'])
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 54b844a..b36bf5c 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -78,11 +78,20 @@
subunit-trace output filter. But, if you would prefer a subunit v2 stream be
output to STDOUT use the **--subunit** flag
+Combining Runs
+==============
+
+There are certain situations in which you want to split a single run of tempest
+across 2 executions of tempest run. (for example to run part of the tests
+serially and others in parallel) To accomplish this but still treat the results
+as a single run you can leverage the **--combine** option which will append
+the current run's results with the previous runs.
"""
import io
import os
import sys
+import tempfile
import threading
from cliff import command
@@ -165,6 +174,12 @@
else:
print("No .testr.conf file was found for local execution")
sys.exit(2)
+ if parsed_args.combine:
+ temp_stream = tempfile.NamedTemporaryFile()
+ return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
+ temp_stream, sys.stderr)
+ if return_code > 0:
+ sys.exit(return_code)
regex = self._build_regex(parsed_args)
if parsed_args.list_tests:
@@ -173,6 +188,16 @@
else:
options = self._build_options(parsed_args)
returncode = self._run(regex, options)
+ if returncode > 0:
+ sys.exit(returncode)
+
+ if parsed_args.combine:
+ return_code = run_argv(['tempest', 'last', '--subunit'], sys.stdin,
+ temp_stream, sys.stderr)
+ if return_code > 0:
+ sys.exit(return_code)
+ returncode = run_argv(['tempest', 'load', temp_stream.name],
+ sys.stdin, sys.stdout, sys.stderr)
sys.exit(returncode)
def get_description(self):
@@ -231,6 +256,10 @@
# output args
parser.add_argument("--subunit", action='store_true',
help='Enable subunit v2 output')
+ parser.add_argument("--combine", action='store_true',
+ help='Combine the output of this run with the '
+ "previous run's as a combined stream in the "
+ "testr repository after it finish")
parser.set_defaults(parallel=True)
return parser
diff --git a/tempest/config.py b/tempest/config.py
index 83c5c0e..7f45cf4 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -133,7 +133,7 @@
cfg.StrOpt('uri_v3',
help='Full URI of the OpenStack Identity API (Keystone), v3'),
cfg.StrOpt('auth_version',
- default='v2',
+ default='v3',
help="Identity API version to be used for authentication "
"for API tests."),
cfg.StrOpt('region',
@@ -225,6 +225,13 @@
deprecated_for_removal=True,
deprecated_reason="All supported version of OpenStack now "
"supports the 'reseller' feature"),
+ # TODO(rodrigods): This is a feature flag for bug 1590578 which is fixed
+ # in Newton and Ocata. This option can be removed after Mitaka is end of
+ # life.
+ cfg.BoolOpt('forbid_global_implied_dsr',
+ default=False,
+ help='Does the environment forbid global roles implying '
+ 'domain specific ones?'),
cfg.BoolOpt('security_compliance',
default=False,
help='Does the environment have the security compliance '
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index bc5d18e..d873402 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -1,4 +1,5 @@
# Copyright 2016 IBM Corp.
+# Copyright 2017 AT&T Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -45,3 +46,41 @@
# list response schema wasn't changed for v2.26 so use v2.1
list_servers = copy.deepcopy(servers21.list_servers)
+
+list_tags = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'tags': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'string'
+ }
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['tags']
+ }
+}
+
+update_all_tags = copy.deepcopy(list_tags)
+
+delete_all_tags = {'status_code': [204]}
+
+check_tag_existence = {'status_code': [204]}
+
+update_tag = {
+ 'status_code': [201, 204],
+ 'response_header': {
+ 'type': 'object',
+ 'properties': {
+ 'location': {
+ 'type': 'string'
+ }
+ },
+ 'required': ['location']
+ }
+}
+
+delete_tag = {'status_code': [204]}
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index 5e65bee..657c0c1 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -111,6 +111,7 @@
except (EOFError,
socket.error, socket.timeout,
paramiko.SSHException) as e:
+ ssh.close()
if self._is_timed_out(_start_time):
LOG.exception("Failed to establish authenticated ssh"
" connection to %s@%s after %d attempts",
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index adff244..c167d81 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -1,5 +1,6 @@
# Copyright 2012 OpenStack Foundation
# Copyright 2013 Hewlett-Packard Development Company, L.P.
+# Copyright 2017 AT&T Corp.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -732,3 +733,92 @@
self.validate_response(security_groups_schema.list_security_groups,
resp, body)
return rest_client.ResponseBody(resp, body)
+
+ def list_tags(self, server_id):
+ """Lists all tags for a server.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#list-tags
+ """
+ url = 'servers/%s/tags' % server_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.list_tags, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_all_tags(self, server_id, tags):
+ """Replaces all tags on specified server with the new set of tags.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#replace-tags
+
+ :param tags: List of tags to replace current server tags with.
+ """
+ url = 'servers/%s/tags' % server_id
+ put_body = {'tags': tags}
+ resp, body = self.put(url, json.dumps(put_body))
+ body = json.loads(body)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.update_all_tags, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_all_tags(self, server_id):
+ """Deletes all tags from the specified server.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#delete-all-tags
+ """
+ url = 'servers/%s/tags' % server_id
+ resp, body = self.delete(url)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.delete_all_tags, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
+ def check_tag_existence(self, server_id, tag):
+ """Checks tag existence on the server.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#check-tag-existence
+
+ :param tag: Check for existence of tag on specified server.
+ """
+ url = 'servers/%s/tags/%s' % (server_id, tag)
+ resp, body = self.get(url)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.check_tag_existence, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_tag(self, server_id, tag):
+ """Adds a single tag to the server if server has no specified tag.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#add-a-single-tag
+
+ :param tag: Tag to be added to the specified server.
+ """
+ url = 'servers/%s/tags/%s' % (server_id, tag)
+ resp, body = self.put(url, None)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.update_tag, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_tag(self, server_id, tag):
+ """Deletes a single tag from the specified server.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#delete-a-single-tag
+
+ :param tag: Tag to be removed from the specified server.
+ """
+ url = 'servers/%s/tags/%s' % (server_id, tag)
+ resp, body = self.delete(url)
+ schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(schema.delete_tag, resp, body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index e58031b..e5f5f68 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -912,7 +912,7 @@
# The target login is assumed to have been configured for
# key-based authentication by cloud-init.
try:
- for net_name, ip_addresses in server['addresses'].items():
+ for ip_addresses in server['addresses'].values():
for ip_address in ip_addresses:
self.check_vm_connectivity(ip_address['addr'],
username,
@@ -926,14 +926,15 @@
def _check_remote_connectivity(self, source, dest, should_succeed=True,
nic=None):
- """check ping server via source ssh connection
+ """assert ping server via source ssh connection
+
+ Note: This is an internal method. Use check_remote_connectivity
+ instead.
:param source: RemoteClient: an ssh connection from which to ping
:param dest: and IP to ping against
:param should_succeed: boolean should ping succeed or not
:param nic: specific network interface to ping from
- :returns: boolean -- should_succeed == ping
- :returns: ping is false if ping failed
"""
def ping_remote():
try:
@@ -948,6 +949,25 @@
CONF.validation.ping_timeout,
1)
+ def check_remote_connectivity(self, source, dest, should_succeed=True,
+ nic=None):
+ """assert ping server via source ssh connection
+
+ :param source: RemoteClient: an ssh connection from which to ping
+ :param dest: and IP to ping against
+ :param should_succeed: boolean should ping succeed or not
+ :param nic: specific network interface to ping from
+ """
+ result = self._check_remote_connectivity(source, dest, should_succeed,
+ nic)
+ source_host = source.ssh_client.host
+ if should_succeed:
+ msg = "Timed out waiting for %s to become reachable from %s" \
+ % (dest, source_host)
+ else:
+ msg = "%s is reachable from %s" % (dest, source_host)
+ self.assertTrue(result, msg)
+
def _create_security_group(self, security_group_rules_client=None,
tenant_id=None,
namestart='secgroup-smoke',
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index 95c2d32..b0b516a 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -82,7 +82,7 @@
aggregate = self.aggregates_client.set_metadata(aggregate['id'],
metadata=meta)
- for key, value in meta.items():
+ for key in meta.keys():
self.assertEqual(meta[key],
aggregate['aggregate']['metadata'][key])
@@ -96,6 +96,7 @@
return aggregate
@decorators.idempotent_id('cb2b4c4f-0c7c-4164-bdde-6285b302a081')
+ @test.attr(type='slow')
@test.services('compute')
def test_aggregate_basic_ops(self):
self.useFixture(fixtures.LockFixture('availability_zone'))
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index da29485..a05b1b1 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -62,6 +62,7 @@
self.nova_volume_detach(server, attached_volume)
@decorators.idempotent_id('79165fb4-5534-4b9d-8429-97ccffb8f86e')
+ @test.attr(type='slow')
@test.services('compute', 'volume', 'image')
def test_encrypted_cinder_volumes_luks(self):
server = self.launch_instance()
@@ -71,6 +72,7 @@
self.attach_detach_volume(server, volume)
@decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
+ @test.attr(type='slow')
@test.services('compute', 'volume', 'image')
def test_encrypted_cinder_volumes_cryptsetup(self):
server = self.launch_instance()
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 27c45cb..5fee801 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -94,7 +94,7 @@
raise exceptions.TimeoutException(msg)
def _get_floating_ip_in_server_addresses(self, floating_ip, server):
- for network_name, addresses in server['addresses'].items():
+ for addresses in server['addresses'].values():
for address in addresses:
if (address['OS-EXT-IPS:type'] == 'floating' and
address['addr'] == floating_ip['ip']):
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 1196659..6665fa7 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -104,6 +104,7 @@
return body['OS-EXT-SRV-ATTR:host']
@decorators.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_stop_start(self):
keypair = self.create_keypair()
@@ -129,6 +130,7 @@
server, keypair, floating_ip)
@decorators.idempotent_id('88a529c2-1daa-4c85-9aec-d541ba3eb699')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_rebuild(self):
keypair = self.create_keypair()
@@ -143,6 +145,7 @@
@decorators.idempotent_id('2b2642db-6568-4b35-b812-eceed3fa20ce')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_pause_unpause(self):
keypair = self.create_keypair()
@@ -160,6 +163,7 @@
@decorators.idempotent_id('5cdf9499-541d-4923-804e-b9a60620a7f0')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_suspend_resume(self):
keypair = self.create_keypair()
@@ -177,6 +181,7 @@
@decorators.idempotent_id('719eb59d-2f42-4b66-b8b1-bb1254473967')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_resize(self):
resize_flavor = CONF.compute.flavor_ref_alt
@@ -200,6 +205,7 @@
@testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
'Less than 2 compute nodes, skipping multinode '
'tests.')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_cold_migration(self):
keypair = self.create_keypair()
@@ -225,6 +231,7 @@
@testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
'Less than 2 compute nodes, skipping multinode '
'tests.')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_cold_migration_revert(self):
keypair = self.create_keypair()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index d8c8d4a..e6a2e9d 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -347,20 +347,8 @@
ip_address, private_key=private_key)
for remote_ip in address_list:
- if should_connect:
- msg = ("Timed out waiting for %s to become "
- "reachable") % remote_ip
- else:
- msg = "ip address %s is reachable" % remote_ip
- try:
- self.assertTrue(self._check_remote_connectivity
- (ssh_source, remote_ip, should_connect),
- msg)
- except Exception:
- LOG.exception("Unable to access {dest} via ssh to "
- "floating-ip {src}".format(dest=remote_ip,
- src=floating_ip))
- raise
+ self.check_remote_connectivity(ssh_source, remote_ip,
+ should_connect)
@test.attr(type='smoke')
@decorators.idempotent_id('f323b3ba-82f8-4db7-8ea6-6a895869ec49')
@@ -418,6 +406,7 @@
@decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
@testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
'No way to calculate MTU for networks')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_mtu_sized_frames(self):
"""Validate that network MTU sized frames fit through."""
@@ -430,6 +419,7 @@
'Connectivity can only be tested when in a '
'multitenant network environment')
@decorators.skip_because(bug="1610994")
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_connectivity_between_vms_on_different_networks(self):
"""Test connectivity between VMs on different networks
@@ -505,6 +495,7 @@
@testtools.skipIf(CONF.network.shared_physical_network,
'Router state can be altered only with multitenant '
'networks capabilities')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_update_router_admin_state(self):
"""Test to update admin state up of router
@@ -538,6 +529,7 @@
'network isolation not available')
@testtools.skipUnless(CONF.scenario.dhcp_client,
"DHCP client is not available.")
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_subnet_details(self):
"""Tests that subnet's extra configuration details are affecting VMs.
@@ -620,6 +612,7 @@
@testtools.skipUnless(CONF.network_feature_enabled.port_admin_state_change,
"Changing a port's admin state is not supported "
"by the test environment")
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_update_instance_port_admin_state(self):
"""Test to update admin_state_up attribute of instance port
@@ -648,23 +641,24 @@
self.check_public_network_connectivity(
should_connect=True, msg="before updating "
"admin_state_up of instance port to False")
- self._check_remote_connectivity(ssh_client, dest=server_pip,
- should_succeed=True)
+ self.check_remote_connectivity(ssh_client, dest=server_pip,
+ should_succeed=True)
self.ports_client.update_port(port_id, admin_state_up=False)
self.check_public_network_connectivity(
should_connect=False, msg="after updating "
"admin_state_up of instance port to False",
should_check_floating_ip_status=False)
- self._check_remote_connectivity(ssh_client, dest=server_pip,
- should_succeed=False)
+ self.check_remote_connectivity(ssh_client, dest=server_pip,
+ should_succeed=False)
self.ports_client.update_port(port_id, admin_state_up=True)
self.check_public_network_connectivity(
should_connect=True, msg="after updating "
"admin_state_up of instance port to True")
- self._check_remote_connectivity(ssh_client, dest=server_pip,
- should_succeed=True)
+ self.check_remote_connectivity(ssh_client, dest=server_pip,
+ should_succeed=True)
@decorators.idempotent_id('759462e1-8535-46b0-ab3a-33aa45c55aaa')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_preserve_preexisting_port(self):
"""Test preserve pre-existing port
@@ -716,6 +710,7 @@
@test.requires_ext(service='network', extension='l3_agent_scheduler')
@decorators.idempotent_id('2e788c46-fb3f-4ac9-8f82-0561555bea73')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_router_rescheduling(self):
"""Tests that router can be removed from agent and add to a new agent.
@@ -795,6 +790,7 @@
@testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
'NIC hotplug not available')
@decorators.idempotent_id('7c0bb1a2-d053-49a4-98f9-ca1a1d849f63')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_port_security_macspoofing_port(self):
"""Tests port_security extension enforces mac spoofing
@@ -833,15 +829,15 @@
spoof_nic = ssh_client.get_nic_name_by_mac(spoof_port["mac_address"])
peer = self._create_server(self.new_net)
peer_address = peer['addresses'][self.new_net['name']][0]['addr']
- self._check_remote_connectivity(ssh_client, dest=peer_address,
- nic=spoof_nic, should_succeed=True)
+ self.check_remote_connectivity(ssh_client, dest=peer_address,
+ nic=spoof_nic, should_succeed=True)
ssh_client.set_mac_address(spoof_nic, spoof_mac)
new_mac = ssh_client.get_mac_address(nic=spoof_nic)
self.assertEqual(spoof_mac, new_mac)
- self._check_remote_connectivity(ssh_client, dest=peer_address,
- nic=spoof_nic, should_succeed=False)
+ self.check_remote_connectivity(ssh_client, dest=peer_address,
+ nic=spoof_nic, should_succeed=False)
self.ports_client.update_port(spoof_port["id"],
port_security_enabled=False,
security_groups=[])
- self._check_remote_connectivity(ssh_client, dest=peer_address,
- nic=spoof_nic, should_succeed=True)
+ self.check_remote_connectivity(ssh_client, dest=peer_address,
+ nic=spoof_nic, should_succeed=True)
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index f33784e..d8a1363 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -110,7 +110,7 @@
@staticmethod
def define_server_ips(srv):
ips = {'4': None, '6': []}
- for net_name, nics in srv['addresses'].items():
+ for nics in srv['addresses'].values():
for nic in nics:
if nic['version'] == 6:
ips['6'].append(nic['addr'])
@@ -191,25 +191,18 @@
self.assertTrue(test_utils.call_until_true(srv2_v6_addr_assigned,
CONF.validation.ping_timeout, 1))
- self._check_connectivity(sshv4_1, ips_from_api_2['4'])
- self._check_connectivity(sshv4_2, ips_from_api_1['4'])
+ self.check_remote_connectivity(sshv4_1, ips_from_api_2['4'])
+ self.check_remote_connectivity(sshv4_2, ips_from_api_1['4'])
for i in range(n_subnets6):
- self._check_connectivity(sshv4_1,
- ips_from_api_2['6'][i])
- self._check_connectivity(sshv4_1,
- self.subnets_v6[i]['gateway_ip'])
- self._check_connectivity(sshv4_2,
- ips_from_api_1['6'][i])
- self._check_connectivity(sshv4_2,
- self.subnets_v6[i]['gateway_ip'])
-
- def _check_connectivity(self, source, dest):
- self.assertTrue(
- self._check_remote_connectivity(source, dest),
- "Timed out waiting for %s to become reachable from %s" %
- (dest, source.ssh_client.host)
- )
+ self.check_remote_connectivity(sshv4_1,
+ ips_from_api_2['6'][i])
+ self.check_remote_connectivity(sshv4_1,
+ self.subnets_v6[i]['gateway_ip'])
+ self.check_remote_connectivity(sshv4_2,
+ ips_from_api_1['6'][i])
+ self.check_remote_connectivity(sshv4_2,
+ self.subnets_v6[i]['gateway_ip'])
@test.attr(type='slow')
@decorators.idempotent_id('2c92df61-29f0-4eaa-bee3-7c65bef62a43')
@@ -247,6 +240,7 @@
def test_dualnet_dhcp6_stateless_from_os(self):
self._prepare_and_test(address6_mode='dhcpv6-stateless', dualnet=True)
+ @test.attr(type='slow')
@decorators.idempotent_id('cf1c4425-766b-45b8-be35-e2959728eb00')
@test.services('compute', 'network')
def test_dualnet_multi_prefix_dhcpv6_stateless(self):
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index c989e01..7fd8c91 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -46,6 +46,7 @@
self.delete_container(container_name)
@decorators.idempotent_id('916c7111-cb1f-44b2-816d-8f760e4ea910')
+ @test.attr(type='slow')
@test.services('object_storage')
def test_swift_acl_anonymous_download(self):
"""This test will cover below steps:
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index a01124d..fa12f33 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -368,20 +368,12 @@
access_point_ssh, private_key=private_key)
return access_point_ssh
- def _check_connectivity(self, access_point, ip, should_succeed=True):
- if should_succeed:
- msg = "Timed out waiting for %s to become reachable" % ip
- else:
- msg = "%s is reachable" % ip
- self.assertTrue(self._check_remote_connectivity(access_point, ip,
- should_succeed), msg)
-
def _test_in_tenant_block(self, tenant):
access_point_ssh = self._connect_to_access_point(tenant)
for server in tenant.servers:
- self._check_connectivity(access_point=access_point_ssh,
- ip=self._get_server_ip(server),
- should_succeed=False)
+ self.check_remote_connectivity(source=access_point_ssh,
+ dest=self._get_server_ip(server),
+ should_succeed=False)
def _test_in_tenant_allow(self, tenant):
ruleset = dict(
@@ -396,8 +388,8 @@
)
access_point_ssh = self._connect_to_access_point(tenant)
for server in tenant.servers:
- self._check_connectivity(access_point=access_point_ssh,
- ip=self._get_server_ip(server))
+ self.check_remote_connectivity(source=access_point_ssh,
+ dest=self._get_server_ip(server))
def _test_cross_tenant_block(self, source_tenant, dest_tenant):
# if public router isn't defined, then dest_tenant access is via
@@ -405,8 +397,8 @@
access_point_ssh = self._connect_to_access_point(source_tenant)
ip = self._get_server_ip(dest_tenant.access_point,
floating=self.floating_ip_access)
- self._check_connectivity(access_point=access_point_ssh, ip=ip,
- should_succeed=False)
+ self.check_remote_connectivity(source=access_point_ssh, dest=ip,
+ should_succeed=False)
def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
"""check for each direction:
@@ -427,7 +419,7 @@
access_point_ssh = self._connect_to_access_point(source_tenant)
ip = self._get_server_ip(dest_tenant.access_point,
floating=self.floating_ip_access)
- self._check_connectivity(access_point_ssh, ip)
+ self.check_remote_connectivity(access_point_ssh, ip)
# test that reverse traffic is still blocked
self._test_cross_tenant_block(dest_tenant, source_tenant)
@@ -444,7 +436,7 @@
access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
ip = self._get_server_ip(source_tenant.access_point,
floating=self.floating_ip_access)
- self._check_connectivity(access_point_ssh_2, ip)
+ self.check_remote_connectivity(access_point_ssh_2, ip)
def _verify_mac_addr(self, tenant):
"""Verify that VM has the same ip, mac as listed in port"""
@@ -502,6 +494,7 @@
raise
@decorators.idempotent_id('f4d556d7-1526-42ad-bafb-6bebf48568f6')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_port_update_new_security_group(self):
"""Verifies the traffic after updating the vm port
@@ -537,9 +530,9 @@
# Check connectivity failure with default security group
try:
access_point_ssh = self._connect_to_access_point(new_tenant)
- self._check_connectivity(access_point=access_point_ssh,
- ip=self._get_server_ip(server),
- should_succeed=False)
+ self.check_remote_connectivity(source=access_point_ssh,
+ dest=self._get_server_ip(server),
+ should_succeed=False)
server_id = server['id']
port_id = self.admin_manager.ports_client.list_ports(
device_id=server_id)['ports'][0]['id']
@@ -547,15 +540,16 @@
# update port with new security group and check connectivity
self.ports_client.update_port(port_id, security_groups=[
new_tenant.security_groups['new_sg']['id']])
- self._check_connectivity(
- access_point=access_point_ssh,
- ip=self._get_server_ip(server))
+ self.check_remote_connectivity(
+ source=access_point_ssh,
+ dest=self._get_server_ip(server))
except Exception:
for tenant in self.tenants.values():
self._log_console_output(servers=tenant.servers)
raise
@decorators.idempotent_id('d2f77418-fcc4-439d-b935-72eca704e293')
+ @test.attr(type='slow')
@test.services('compute', 'network')
def test_multiple_security_groups(self):
"""Verify multiple security groups and checks that rules
@@ -587,6 +581,7 @@
private_key=private_key,
should_connect=True)
+ @test.attr(type='slow')
@test.requires_ext(service='network', extension='port-security')
@decorators.idempotent_id('7c811dcc-263b-49a3-92d2-1b4d8405f50c')
@test.services('compute', 'network')
@@ -612,21 +607,22 @@
self.ports_client.update_port(port_id,
port_security_enabled=True,
security_groups=[])
- self._check_connectivity(access_point=access_point_ssh,
- ip=self._get_server_ip(server),
- should_succeed=False)
+ self.check_remote_connectivity(source=access_point_ssh,
+ dest=self._get_server_ip(server),
+ should_succeed=False)
self.ports_client.update_port(port_id,
port_security_enabled=False,
security_groups=[])
- self._check_connectivity(
- access_point=access_point_ssh,
- ip=self._get_server_ip(server))
+ self.check_remote_connectivity(
+ source=access_point_ssh,
+ dest=self._get_server_ip(server))
except Exception:
for tenant in self.tenants.values():
self._log_console_output(servers=tenant.servers)
raise
+ @test.attr(type='slow')
@test.requires_ext(service='network', extension='port-security')
@decorators.idempotent_id('13ccf253-e5ad-424b-9c4a-97b88a026699')
@testtools.skipUnless(
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index ec839cd..1960e9a 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -48,6 +48,7 @@
cls.set_network_resources()
super(TestServerAdvancedOps, cls).setup_credentials()
+ @test.attr(type='slow')
@decorators.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
@@ -69,6 +70,7 @@
waiters.wait_for_server_status(self.servers_client, instance_id,
'ACTIVE')
+ @test.attr(type='slow')
@decorators.idempotent_id('949da7d5-72c8-4808-8802-e3d70df98e2c')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 75cef88..9e763f8 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -74,6 +74,7 @@
private_key=keypair['private_key'])
self.assertEqual(timestamp, timestamp2)
+ @test.attr(type='slow')
@decorators.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@@ -81,6 +82,7 @@
def test_shelve_instance(self):
self._create_server_then_shelve_and_unshelve()
+ @test.attr(type='slow')
@decorators.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 6dedd1d..a699de2 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -41,6 +41,7 @@
raise cls.skipException("Snapshotting is not available.")
@decorators.idempotent_id('608e604b-1d63-4a82-8e3e-91bc665c90b4')
+ @test.attr(type='slow')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@test.services('compute', 'network', 'image')
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 716c0bf..aabb767 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -88,6 +88,7 @@
CONF.compute.build_interval):
raise lib_exc.TimeoutException
+ @test.attr(type='slow')
@decorators.skip_because(bug="1664793")
@decorators.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
@testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index b72dae9..8cab19c 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -95,7 +95,6 @@
waiters.wait_for_server_termination(self.servers_client, server['id'])
@decorators.idempotent_id('557cd2c2-4eb8-4dce-98be-f86765ff311b')
- @test.attr(type='smoke')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@test.services('compute', 'volume', 'image')
@@ -177,6 +176,7 @@
self.assertEqual(timestamp, timestamp3)
@decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
+ @test.attr(type='slow')
@test.services('compute', 'image', 'volume')
def test_create_server_from_volume_snapshot(self):
# Create a volume from an image
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index 891e22d..f580ea6 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -91,6 +91,7 @@
waiters.wait_for_volume_retype(self.volumes_client,
volume_id, new_volume_type)
+ @test.attr(type='slow')
@decorators.idempotent_id('deadd2c2-beef-4dce-98be-f86765ff311b')
@test.services('compute', 'volume')
def test_volume_migrate_attached(self):
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index b563ab2..8d391c1 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -1,4 +1,5 @@
# Copyright 2015 IBM Corp.
+# Copyright 2017 AT&T Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -14,6 +15,9 @@
import copy
+import mock
+
+from tempest.lib.services.compute import base_compute_client
from tempest.lib.services.compute import servers_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -186,6 +190,9 @@
FAKE_REBUILD_SERVER = copy.deepcopy(FAKE_SERVER_GET)
FAKE_REBUILD_SERVER['server']['adminPass'] = 'fake-admin-pass'
+ FAKE_TAGS = ["foo", "bar"]
+ REPLACE_FAKE_TAGS = ["baz", "qux"]
+
server_id = FAKE_SERVER_GET['server']['id']
network_id = 'a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb'
@@ -194,6 +201,7 @@
fake_auth = fake_auth_provider.FakeAuthProvider()
self.client = servers_client.ServersClient(
fake_auth, 'compute', 'regionOne')
+ self.addCleanup(mock.patch.stopall)
def test_list_servers_with_str_body(self):
self._test_list_servers()
@@ -1031,3 +1039,113 @@
{'security_groups': self.FAKE_SECURITY_GROUPS},
server_id=self.server_id,
)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_list_tags_str_body(self, _):
+ self._test_list_tags()
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_list_tags_byte_body(self, _):
+ self._test_list_tags(bytes_body=True)
+
+ def _test_list_tags(self, bytes_body=False):
+ expected = {"tags": self.FAKE_TAGS}
+ self.check_service_client_function(
+ self.client.list_tags,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ expected,
+ server_id=self.server_id,
+ to_utf=bytes_body)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_update_all_tags_str_body(self, _):
+ self._test_update_all_tags()
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_update_all_tags_byte_body(self, _):
+ self._test_update_all_tags(bytes_body=True)
+
+ def _test_update_all_tags(self, bytes_body=False):
+ expected = {"tags": self.REPLACE_FAKE_TAGS}
+ self.check_service_client_function(
+ self.client.update_all_tags,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ expected,
+ server_id=self.server_id,
+ tags=self.REPLACE_FAKE_TAGS,
+ to_utf=bytes_body)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_delete_all_tags(self, _):
+ self.check_service_client_function(
+ self.client.delete_all_tags,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ server_id=self.server_id,
+ status=204)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_check_tag_existence_str_body(self, _):
+ self._test_check_tag_existence()
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_check_tag_existence_byte_body(self, _):
+ self._test_check_tag_existence(bytes_body=True)
+
+ def _test_check_tag_existence(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.check_tag_existence,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ {},
+ server_id=self.server_id,
+ tag=self.FAKE_TAGS[0],
+ status=204,
+ to_utf=bytes_body)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_update_tag_str_body(self, _):
+ self._test_update_tag()
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_update_tag_byte_body(self, _):
+ self._test_update_tag(bytes_body=True)
+
+ def _test_update_tag(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_tag,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ {},
+ server_id=self.server_id,
+ tag=self.FAKE_TAGS[0],
+ status=201,
+ headers={'location': 'fake_location'},
+ to_utf=bytes_body)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_delete_tag_str_body(self, _):
+ self._test_delete_tag()
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.26'))
+ def test_delete_tag_byte_body(self, _):
+ self._test_delete_tag(bytes_body=True)
+
+ def _test_delete_tag(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.delete_tag,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ server_id=self.server_id,
+ tag=self.FAKE_TAGS[0],
+ status=204,
+ to_utf=bytes_body)
diff --git a/test-requirements.txt b/test-requirements.txt
index f7d63a8..936d5aa 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,9 +1,9 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-hacking<0.13,>=0.12.0 # Apache-2.0
+hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0
# needed for doc build
-sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
+sphinx>=1.5.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
reno>=1.8.0 # Apache-2.0
mock>=2.0 # BSD
diff --git a/tools/tox_install.sh b/tools/tox_install.sh
new file mode 100755
index 0000000..43468e4
--- /dev/null
+++ b/tools/tox_install.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+# Client constraint file contains this client version pin that is in conflict
+# with installing the client from source. We should remove the version pin in
+# the constraints file before applying it for from-source installation.
+
+CONSTRAINTS_FILE=$1
+shift 1
+
+set -e
+
+# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
+# published to logs.openstack.org for easy debugging.
+localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
+
+if [[ $CONSTRAINTS_FILE != http* ]]; then
+ CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
+fi
+# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
+curl $CONSTRAINTS_FILE --insecure --progress-bar --output $localfile
+
+pip install -c$localfile openstack-requirements
+
+# This is the main purpose of the script: Allow local installation of
+# the current repo. It is listed in constraints file and thus any
+# install will be constrained and we need to unconstrain it.
+edit-constraints $localfile -- $CLIENT_NAME
+
+pip install -c$localfile -U $*
+exit $?
diff --git a/tox.ini b/tox.ini
index d8d390e..05331fa 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,6 +8,8 @@
setenv =
VIRTUAL_ENV={envdir}
OS_TEST_PATH=./tempest/test_discover
+ BRANCH_NAME=master
+ CLIENT_NAME=tempest
deps =
setuptools
-r{toxinidir}/requirements.txt
@@ -17,9 +19,12 @@
VIRTUAL_ENV={envdir}
OS_TEST_PATH=./tempest/tests
PYTHONWARNINGS=default::DeprecationWarning
-passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
+ BRANCH_NAME=master
+ CLIENT_NAME=tempest
+passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION
usedevelop = True
-install_command = pip install -U {opts} {packages}
+install_command =
+ {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
whitelist_externals = *
deps =
-r{toxinidir}/requirements.txt
@@ -78,7 +83,8 @@
# See the testrepository bug: https://bugs.launchpad.net/testrepository/+bug/1208610
commands =
find . -type f -name "*.pyc" -delete
- tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))' {posargs}
+ tempest run --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.api)' {posargs}
+ tempest run --combine --serial --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.scenario)' {posargs}
[testenv:full-serial]
envdir = .tox/tempest