Merge "trivial: Fix typos in the test_removal documentation page"
diff --git a/.zuul.yaml b/.zuul.yaml
index 8ab3028..fd3aa2a 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -144,8 +144,11 @@
# TODO(gmann): needs to migrate this to zuulv3
- job:
- name: tempest-scenario-multinode-lvm-multibackend
+ name: tempest-scenario-all
parent: legacy-dsvm-base-multinode
+ description: |
+ This job will run all scenario tests including slow tests
+ with lvm multibackend setup. This job will not run any API tests.
run: playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
post-run: playbooks/tempest-scenario-multinode-lvm-multibackend/post.yaml
timeout: 10800
@@ -336,8 +339,7 @@
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
- tempest-tox-plugin-sanity-check
- - tempest-scenario-multinode-lvm-multibackend:
- voting: false
+ - tempest-scenario-all:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^.*\.rst$
@@ -347,6 +349,7 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
+ - ^tempest/api/.*$
- nova-cells-v1:
irrelevant-files:
- ^(test-|)requirements.txt$
@@ -357,12 +360,21 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
+ - nova-live-migration:
+ voting: false
+ irrelevant-files:
+ - ^(test-|)requirements.txt$
+ - ^.*\.rst$
+ - ^doc/.*$
+ - ^etc/.*$
+ - ^releasenotes/.*$
+ - ^setup.cfg$
+ - ^tempest/hacking/.*$
+ - ^tempest/tests/.*$
gate:
jobs:
- nova-multiattach
- experimental:
- jobs:
- - nova-live-migration:
+ - tempest-scenario-all:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^.*\.rst$
@@ -372,6 +384,9 @@
- ^setup.cfg$
- ^tempest/hacking/.*$
- ^tempest/tests/.*$
+ - ^tempest/api/.*$
+ experimental:
+ jobs:
- tempest-cinder-v2-api:
irrelevant-files:
- ^(test-|)requirements.txt$
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index ea868ae..6dd00d3 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -358,22 +358,30 @@
.. _2.49: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id44
+ * `2.53`_
+
+ .. _2.53: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-pike
+
* `2.54`_
- .. _2.54: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id4
+ .. _2.54: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id49
* `2.55`_
- .. _2.55: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id49
+ .. _2.55: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id50
* `2.57`_
- .. _2.57: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id51
+ .. _2.57: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id52
* `2.60`_
.. _2.60: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-queens
+ * `2.61`_
+
+ .. _2.61: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id55
+
* `2.63`_
.. _2.63: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id57
diff --git a/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml b/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
index 03f64f9..57b4074 100644
--- a/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
+++ b/playbooks/tempest-scenario-multinode-lvm-multibackend/run.yaml
@@ -47,8 +47,8 @@
set -x
export PYTHONUNBUFFERED=true
export DEVSTACK_GATE_TEMPEST=1
- # Run scenario and nova migration tests with concurrency 2
- export DEVSTACK_GATE_TEMPEST_REGEX='(^tempest\.(scenario|api\.compute\.admin\.test_(live_|)migration))'
+ # Run all scenario tests including slow tests with concurrency 2
+ export DEVSTACK_GATE_TEMPEST_REGEX='(^tempest\.(scenario))'
export TEMPEST_CONCURRENCY=2
export DEVSTACK_GATE_NEUTRON=1
export DEVSTACK_GATE_TLSPROXY=1
diff --git a/releasenotes/notes/tempest-lib-compute-update-service-6019d2dcfe4a1c5d.yaml b/releasenotes/notes/tempest-lib-compute-update-service-6019d2dcfe4a1c5d.yaml
new file mode 100644
index 0000000..d67cdb8
--- /dev/null
+++ b/releasenotes/notes/tempest-lib-compute-update-service-6019d2dcfe4a1c5d.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - |
+ The ``update_service`` API is added to the ``services_client`` compute
+ library. This API is introduced in microversion 2.53 and supersedes
+ the following APIs:
+
+ * ``PUT /os-services/disable`` (``disable_service``)
+ * ``PUT /os-services/disable-log-reason`` (``disable_log_reason``)
+ * ``PUT /os-services/enable`` (``enable_service``)
+ * ``PUT /os-services/force-down`` (``update_forced_down``)
diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py
index 027af25..9f014e6 100644
--- a/tempest/api/compute/admin/test_flavors_microversions.py
+++ b/tempest/api/compute/admin/test_flavors_microversions.py
@@ -41,3 +41,11 @@
self.flavors_client.list_flavors(detail=True)['flavors']
# Checking list API response schema
self.flavors_client.list_flavors()['flavors']
+
+
+class FlavorsV261TestJSON(FlavorsV255TestJSON):
+ min_microversion = '2.61'
+ max_microversion = 'latest'
+
+ # NOTE(gmann): This class tests the flavors APIs
+ # response schema for the 2.61 microversion.
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 201670a..993c8ec 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -13,12 +13,14 @@
# under the License.
from tempest.api.compute import base
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
class ServicesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
"""Tests Services API. List and Enable/Disable require admin privileges."""
+ max_microversion = '2.52'
@classmethod
def setup_clients(cls):
@@ -35,7 +37,8 @@
@decorators.attr(type=['negative'])
@decorators.idempotent_id('d0884a69-f693-4e79-a9af-232d15643bf7')
def test_get_service_by_invalid_params(self):
- # return all services if send the request with invalid parameter
+ # Expect all services to be returned when the request contains invalid
+ # parameters.
services = self.client.list_services()['services']
services_xxx = (self.client.list_services(xxx='nova-compute')
['services'])
@@ -58,3 +61,45 @@
services = self.client.list_services(host='xxx',
binary=binary_name)['services']
self.assertEmpty(services)
+
+
+class ServicesAdminNegativeV253TestJSON(ServicesAdminNegativeTestJSON):
+ min_microversion = '2.53'
+ max_microversion = 'latest'
+
+ # NOTE(felipemonteiro): This class tests the services APIs response schema
+ # for the 2.53 microversion. Schema testing is done for `list_services`
+ # tests.
+
+ @classmethod
+ def resource_setup(cls):
+ super(ServicesAdminNegativeV253TestJSON, cls).resource_setup()
+ # Nova returns 400 if `binary` is not nova-compute.
+ cls.binary = 'nova-compute'
+ cls.fake_service_id = data_utils.rand_uuid()
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('508671aa-c929-4479-bd10-8680d40dd0a6')
+ def test_enable_service_with_invalid_service_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.client.update_service,
+ service_id=self.fake_service_id,
+ status='enabled')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('a9eeeade-42b3-419f-87aa-c9342aa068cf')
+ def test_disable_service_with_invalid_service_id(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.client.update_service,
+ service_id=self.fake_service_id,
+ status='disabled')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('f46a9d91-1e85-4b96-8e7a-db7706fa2e9a')
+ def test_disable_log_reason_with_invalid_service_id(self):
+ # disabled_reason requires that status='disabled' be provided.
+ self.assertRaises(lib_exc.NotFound,
+ self.client.update_service,
+ service_id=self.fake_service_id,
+ status='disabled',
+ disabled_reason='maintenance')
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index f9050a8..81635ca 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -84,6 +84,6 @@
@decorators.idempotent_id('45fbe5e0-acb5-49aa-837a-ff8d0719db91')
def test_create_keypair_invalid_name(self):
# Keypairs with name being an invalid name should not be created
- k_name = 'key_/.\@:'
+ k_name = r'key_/.\@:'
self.assertRaises(lib_exc.BadRequest, self.create_keypair,
k_name)
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 14aecfd..3dffd01 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -227,7 +227,7 @@
@decorators.idempotent_id('24a89b0c-0d55-4a28-847f-45075f19b27b')
def test_list_servers_filtered_by_name_regex(self):
# list of regex that should match s1, s2 and s3
- regexes = ['^.*\-instance\-[0-9]+$', '^.*\-instance\-.*$']
+ regexes = [r'^.*\-instance\-[0-9]+$', r'^.*\-instance\-.*$']
for regex in regexes:
params = {'name': regex}
body = self.client.list_servers(**params)
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index 532f0d7..8ae43d6 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -19,7 +19,6 @@
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
CONF = config.CONF
@@ -28,30 +27,6 @@
credentials = ['primary', 'admin', 'alt']
- @decorators.idempotent_id('0f9f5a5f-d5cd-4a86-8a5b-c5ded151f212')
- def test_tokens(self):
- # Valid user's token is authenticated
- # Create a User
- u_name = data_utils.rand_name('user')
- u_desc = '%s-description' % u_name
- u_password = data_utils.rand_password()
- user = self.create_test_user(
- name=u_name, description=u_desc, password=u_password)
- # Perform Authentication
- resp = self.token.auth(user_id=user['id'],
- password=u_password).response
- subject_token = resp['x-subject-token']
- self.client.check_token_existence(subject_token)
- # Perform GET Token
- token_details = self.client.show_token(subject_token)['token']
- self.assertEqual(resp['x-subject-token'], subject_token)
- self.assertEqual(token_details['user']['id'], user['id'])
- self.assertEqual(token_details['user']['name'], u_name)
- # Perform Delete Token
- self.client.delete_token(subject_token)
- self.assertRaises(lib_exc.NotFound, self.client.check_token_existence,
- subject_token)
-
@decorators.idempotent_id('565fa210-1da1-4563-999b-f7b5b67cf112')
def test_rescope_token(self):
"""Rescope a token.
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index 4c72d82..f13aa10 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -91,3 +91,28 @@
self.assertIsNotNone(subject_name, 'Expected user name in token.')
self.assertEqual(resp['methods'][0], 'password')
+
+ @decorators.idempotent_id('0f9f5a5f-d5cd-4a86-8a5b-c5ded151f212')
+ def test_token_auth_creation_existence_deletion(self):
+ # Tests basic token auth functionality in a way that is compatible with
+ # pre-provisioned credentials. The default user is used for token
+ # authentication.
+
+ # Valid user's token is authenticated
+ user = self.os_primary.credentials
+ # Perform Authentication
+ resp = self.non_admin_token.auth(
+ user_id=user.user_id, password=user.password).response
+ subject_token = resp['x-subject-token']
+ self.non_admin_client.check_token_existence(subject_token)
+ # Perform GET Token
+ token_details = self.non_admin_client.show_token(
+ subject_token)['token']
+ self.assertEqual(resp['x-subject-token'], subject_token)
+ self.assertEqual(token_details['user']['id'], user.user_id)
+ self.assertEqual(token_details['user']['name'], user.username)
+ # Perform Delete Token
+ self.non_admin_client.delete_token(subject_token)
+ self.assertRaises(lib_exc.NotFound,
+ self.non_admin_client.check_token_existence,
+ subject_token)
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 6849653..e79f8c3 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -59,7 +59,7 @@
# Try to create a third network while the quota is two
with self.assertRaisesRegex(
lib_exc.Conflict,
- "Quota exceeded for resources: \['network'\].*"):
+ r"Quota exceeded for resources: \['network'\].*"):
n3 = self.networks_client.create_network()
self.addCleanup(self.networks_client.delete_network,
n3['network']['id'])
diff --git a/tempest/api/volume/admin/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
index 20c3538..9907497 100644
--- a/tempest/api/volume/admin/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -62,8 +62,16 @@
return message_id
@decorators.idempotent_id('50f29e6e-f363-42e1-8ad1-f67ae7fd4d5a')
- def test_list_messages(self):
- self._create_user_message()
+ def test_list_show_messages(self):
+ message_id = self._create_user_message()
+ self.addCleanup(self.messages_client.delete_message, message_id)
+
+ # show message
+ message = self.messages_client.show_message(message_id)['message']
+ for key in MESSAGE_KEYS:
+ self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
+
+ # list messages
messages = self.messages_client.list_messages()['messages']
self.assertIsInstance(messages, list)
for message in messages:
@@ -71,16 +79,6 @@
self.assertIn(key, message.keys(),
'Missing expected key %s' % key)
- @decorators.idempotent_id('55a4a61e-c7b2-4ba0-a05d-b914bdef3070')
- def test_show_message(self):
- message_id = self._create_user_message()
- self.addCleanup(self.messages_client.delete_message, message_id)
-
- message = self.messages_client.show_message(message_id)['message']
-
- for key in MESSAGE_KEYS:
- self.assertIn(key, message.keys(), 'Missing expected key %s' % key)
-
@decorators.idempotent_id('c6eb6901-cdcc-490f-b735-4fe251842aed')
def test_delete_message(self):
message_id = self._create_user_message()
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index 1c671ec..9be8ee2 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -311,5 +311,6 @@
resources.extend(generate_resources(cred_provider, opts.admin))
dump_accounts(resources, opts.identity_version, opts.accounts)
+
if __name__ == "__main__":
main()
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index 84c8631..d84f3a3 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -26,7 +26,7 @@
LOG = logging.getLogger(__name__)
-STESTR_CONF = """[DEFAULT]
+STESTR_CONF = r"""[DEFAULT]
test_path=%s
top_dir=%s
group_regex=([^\.]*\.)*
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index a4402fe..8dcf575 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -95,7 +95,7 @@
ip_re = re.compile(r'(^|[^0-9])[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]'
'{1,3}([^0-9]|$)')
url_re = re.compile(r'.*INFO.*Request \((?P<name>.*)\): (?P<code>[\d]{3}) '
- '(?P<verb>\w*) (?P<url>.*) .*')
+ r'(?P<verb>\w*) (?P<url>.*) .*')
port_re = re.compile(r'.*:(?P<port>\d+).*')
path_re = re.compile(r'http[s]?://[^/]*/(?P<path>.*)')
request_re = re.compile(r'.* Request - Headers: (?P<headers>.*)')
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 15af271..aa333b3 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -488,5 +488,6 @@
traceback.print_exc()
raise
+
if __name__ == "__main__":
main()
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 75db155..c6e5dcb 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -210,6 +210,7 @@
except exceptions.InvalidConfiguration:
return False
+
# === Credentials
# Type of credentials available from configuration
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index ed11b21..c702d88 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -225,9 +225,9 @@
elif key in ('content-type', 'date', 'last-modified',
'x-copied-from-last-modified') and not value:
return InvalidFormat(key, value)
- elif key == 'x-timestamp' and not re.match("^\d+\.?\d*\Z", value):
+ elif key == 'x-timestamp' and not re.match(r"^\d+\.?\d*\Z", value):
return InvalidFormat(key, value)
- elif key == 'x-copied-from' and not re.match("\S+/\S+", value):
+ elif key == 'x-copied-from' and not re.match(r"\S+/\S+", value):
return InvalidFormat(key, value)
elif key == 'x-trans-id' and \
not re.match("^tx[0-9a-f]{21}-[0-9a-f]{10}.*", value):
diff --git a/tempest/common/utils/__init__.py b/tempest/common/utils/__init__.py
index 225a713..167bf5b 100644
--- a/tempest/common/utils/__init__.py
+++ b/tempest/common/utils/__init__.py
@@ -40,6 +40,7 @@
self.__dict__[attr] = attr_obj
return attr_obj
+
data_utils = DataUtils()
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index b6e7f8c..a57a360 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -287,10 +287,10 @@
if pep8.noqa(physical_line):
return
- if not re.match('class .*Test.*\(.*Admin.*\):', logical_line):
+ if not re.match(r'class .*Test.*\(.*Admin.*\):', logical_line):
return
- if not re.match('.\/tempest\/api\/.*\/admin\/.*', filename):
+ if not re.match(r'.\/tempest\/api\/.*\/admin\/.*', filename):
msg = 'T115: All admin tests should exist under admin path.'
yield(0, msg)
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors.py b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
index af5e67f..43e80cc 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors.py
@@ -82,10 +82,6 @@
}
}
-unset_flavor_extra_specs = {
- 'status_code': [200]
-}
-
create_update_get_flavor_details = {
'status_code': [200],
'response_body': {
diff --git a/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py b/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
index a438d48..3aa1eda 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/flavors_extra_specs.py
@@ -20,7 +20,7 @@
'extra_specs': {
'type': 'object',
'patternProperties': {
- '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+ r'^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
}
}
},
@@ -29,12 +29,16 @@
}
}
+unset_flavor_extra_specs = {
+ 'status_code': [200]
+}
+
set_get_flavor_extra_specs_key = {
'status_code': [200],
'response_body': {
'type': 'object',
'patternProperties': {
- '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+ r'^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
}
}
}
diff --git a/tempest/lib/api_schema/response/compute/v2_11/services.py b/tempest/lib/api_schema/response/compute/v2_11/services.py
index 18b833b..9ece1f9 100644
--- a/tempest/lib/api_schema/response/compute/v2_11/services.py
+++ b/tempest/lib/api_schema/response/compute/v2_11/services.py
@@ -44,3 +44,10 @@
'required': ['service']
}
}
+
+# **** Schemas unchanged in microversion 2.11 since microversion 2.1 ****
+# Note(felipemonteiro): Below are the unchanged schema in this microversion. We
+# need to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+enable_disable_service = copy.deepcopy(services.enable_disable_service)
+disable_log_reason = copy.deepcopy(services.disable_log_reason)
diff --git a/tempest/lib/api_schema/response/compute/v2_47/servers.py b/tempest/lib/api_schema/response/compute/v2_47/servers.py
index 935be70..5d6d4c3 100644
--- a/tempest/lib/api_schema/response/compute/v2_47/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_47/servers.py
@@ -26,7 +26,7 @@
'extra_specs': {
'type': 'object',
'patternProperties': {
- '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
+ r'^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'}
}
}
},
diff --git a/tempest/lib/api_schema/response/compute/v2_53/__init__.py b/tempest/lib/api_schema/response/compute/v2_53/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_53/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_53/services.py b/tempest/lib/api_schema/response/compute/v2_53/services.py
new file mode 100644
index 0000000..aa132a9
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_53/services.py
@@ -0,0 +1,70 @@
+# Copyright 2018 AT&T Corporation.
+# 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_11 import services \
+ as servicesv211
+
+# ***************** Schemas changed in microversion 2.53 *****************
+
+# NOTE(felipemonteiro): This is schema for microversion 2.53 which includes:
+#
+# * changing the service 'id' to 'string' type only
+# * adding update_service which supersedes enable_service, disable_service,
+# disable_log_reason, update_forced_down.
+
+list_services = copy.deepcopy(servicesv211.list_services)
+# The ID of the service is a uuid, so v2.1 pattern does not apply.
+list_services['response_body']['properties']['services']['items'][
+ 'properties']['id'] = {'type': 'string', 'format': 'uuid'}
+
+update_service = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'service': {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': 'string', 'format': 'uuid'},
+ 'binary': {'type': 'string'},
+ 'disabled_reason': {'type': 'string'},
+ 'host': {'type': 'string'},
+ 'state': {'type': 'string'},
+ 'status': {'type': 'string'},
+ 'updated_at': parameter_types.date_time,
+ 'zone': {'type': 'string'},
+ 'forced_down': {'type': 'boolean'}
+ },
+ 'additionalProperties': False,
+ 'required': ['id', 'binary', 'disabled_reason', 'host',
+ 'state', 'status', 'updated_at', 'zone',
+ 'forced_down']
+ }
+ },
+ 'additionalProperties': False,
+ 'required': ['service']
+ }
+}
+
+# **** Schemas unchanged in microversion 2.53 since microversion 2.11 ****
+# Note(felipemonteiro): Below are the unchanged schema in this microversion. We
+# need to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+enable_disable_service = copy.deepcopy(servicesv211.enable_disable_service)
+update_forced_down = copy.deepcopy(servicesv211.update_forced_down)
+disable_log_reason = copy.deepcopy(servicesv211.disable_log_reason)
diff --git a/tempest/lib/api_schema/response/compute/v2_61/__init__.py b/tempest/lib/api_schema/response/compute/v2_61/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_61/__init__.py
diff --git a/tempest/lib/api_schema/response/compute/v2_61/flavors.py b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
new file mode 100644
index 0000000..381fb64
--- /dev/null
+++ b/tempest/lib/api_schema/response/compute/v2_61/flavors.py
@@ -0,0 +1,102 @@
+# Copyright 2018 NEC Corporation. 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+from tempest.lib.api_schema.response.compute.v2_55 import flavors \
+ as flavorsv255
+
+# ****** Schemas changed in microversion 2.61 *****************
+
+# Note(gmann): This is schema for microversion 2.61 which includes the
+# Flavor extra_specs in the Response body of the following APIs:
+# - ``PUT /flavors/{flavor_id}``
+# - ``GET /flavors/detail``
+# - ``GET /flavors/{flavor_id}``
+# - ``POST /flavors``
+
+flavor_description = {
+ 'type': ['string', 'null'],
+ 'minLength': 0, 'maxLength': 65535
+}
+
+flavor_extra_specs = {
+ 'type': 'object',
+ 'patternProperties': {
+ '^[a-zA-Z0-9-_:. ]{1,255}$': {'type': 'string'}
+ }
+}
+
+common_flavor_info = {
+ 'type': 'object',
+ 'properties': {
+ 'name': {'type': 'string'},
+ 'links': parameter_types.links,
+ 'ram': {'type': 'integer'},
+ 'vcpus': {'type': 'integer'},
+ # 'swap' attributes comes as integer value but if it is empty
+ # it comes as "". So defining type of as string and integer.
+ 'swap': {'type': ['integer', 'string']},
+ 'disk': {'type': 'integer'},
+ 'id': {'type': 'string'},
+ 'OS-FLV-DISABLED:disabled': {'type': 'boolean'},
+ 'os-flavor-access:is_public': {'type': 'boolean'},
+ 'rxtx_factor': {'type': 'number'},
+ 'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'},
+ 'description': flavor_description,
+ 'extra_specs': flavor_extra_specs
+ },
+ 'additionalProperties': False,
+ # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and
+ # 'OS-FLV-EXT-DATA' are API extensions. so they are not 'required'.
+ 'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id',
+ 'description']
+}
+
+list_flavors_details = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'flavors': {
+ 'type': 'array',
+ 'items': common_flavor_info
+ },
+ # NOTE(gmann): flavors_links attribute is not necessary
+ # to be present always so it is not 'required'.
+ 'flavors_links': parameter_types.links
+ },
+ 'additionalProperties': False,
+ 'required': ['flavors']
+ }
+}
+
+create_update_get_flavor_details = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'flavor': common_flavor_info
+ },
+ 'additionalProperties': False,
+ 'required': ['flavor']
+ }
+}
+
+# ****** Schemas unchanged in microversion 2.61 since microversion 2.55 ***
+# Note(gmann): Below are the unchanged schema in this microversion. We need
+# to keep this schema in this file to have the generic way to select the
+# right schema based on self.schema_versions_info mapping in service client.
+list_flavors = copy.deepcopy(flavorsv255.list_flavors)
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
index a7d5e49..45d41c7 100644
--- a/tempest/lib/cli/output_parser.py
+++ b/tempest/lib/cli/output_parser.py
@@ -25,7 +25,7 @@
LOG = logging.getLogger(__name__)
-delimiter_line = re.compile('^\+\-[\+\-]+\-\+$')
+delimiter_line = re.compile(r'^\+\-[\+\-]+\-\+$')
def details_multiple(output_lines, with_label=False):
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index d1f0888..82fcd0b 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -358,5 +358,6 @@
"Run 'tox -v -e uuidgen' to automatically fix tests with\n"
"missing @decorators.idempotent_id decorators.")
+
if __name__ == '__main__':
run()
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 4923d7e..2fad0a4 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -23,6 +23,8 @@
as schema_extra_specs
from tempest.lib.api_schema.response.compute.v2_55 import flavors \
as schemav255
+from tempest.lib.api_schema.response.compute.v2_61 import flavors \
+ as schemav261
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
@@ -31,7 +33,8 @@
schema_versions_info = [
{'min': None, 'max': '2.54', 'schema': schema},
- {'min': '2.55', 'max': None, 'schema': schemav255}]
+ {'min': '2.55', 'max': '2.60', 'schema': schemav255},
+ {'min': '2.61', 'max': None, 'schema': schemav261}]
def list_flavors(self, detail=False, **params):
"""Lists flavors.
@@ -202,7 +205,8 @@
"""
resp, body = self.delete('flavors/%s/os-extra_specs/%s' %
(flavor_id, key))
- self.validate_response(schema.unset_flavor_extra_specs, resp, body)
+ self.validate_response(schema_extra_specs.unset_flavor_extra_specs,
+ resp, body)
return rest_client.ResponseBody(resp, body)
def list_flavor_access(self, flavor_id):
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index b046c35..d52de3a 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -20,6 +20,8 @@
from tempest.lib.api_schema.response.compute.v2_1 import services as schema
from tempest.lib.api_schema.response.compute.v2_11 import services \
as schemav211
+from tempest.lib.api_schema.response.compute.v2_53 import services \
+ as schemav253
from tempest.lib.common import rest_client
from tempest.lib.services.compute import base_compute_client
@@ -28,7 +30,8 @@
schema_versions_info = [
{'min': None, 'max': '2.10', 'schema': schema},
- {'min': '2.11', 'max': None, 'schema': schemav211}]
+ {'min': '2.11', 'max': '2.52', 'schema': schemav211},
+ {'min': '2.53', 'max': None, 'schema': schemav253}]
def list_services(self, **params):
"""Lists all running Compute services for a tenant.
@@ -47,9 +50,30 @@
self.validate_response(_schema.list_services, resp, body)
return rest_client.ResponseBody(resp, body)
+ def update_service(self, service_id, **kwargs):
+ """Update a compute service.
+
+ Update a compute service to enable or disable scheduling, including
+ recording a reason why a compute service was disabled from scheduling.
+
+ This API is available starting with microversion 2.53.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#update-compute-service
+ """
+ put_body = json.dumps(kwargs)
+ resp, body = self.put('os-services/%s' % service_id, put_body)
+ body = json.loads(body)
+ _schema = self.get_schema(self.schema_versions_info)
+ self.validate_response(_schema.update_service, resp, body)
+ return rest_client.ResponseBody(resp, body)
+
def enable_service(self, **kwargs):
"""Enable service on a host.
+ ``update_service`` supersedes this API starting with microversion 2.53.
+
For a full list of available parameters, please refer to the official
API reference:
https://developer.openstack.org/api-ref/compute/#enable-scheduling-for-a-compute-service
@@ -63,6 +87,8 @@
def disable_service(self, **kwargs):
"""Disable service on a host.
+ ``update_service`` supersedes this API starting with microversion 2.53.
+
For a full list of available parameters, please refer to the official
API reference:
https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service
@@ -76,6 +102,8 @@
def disable_log_reason(self, **kwargs):
"""Disables scheduling for a Compute service and logs reason.
+ ``update_service`` supersedes this API starting with microversion 2.53.
+
For a full list of available parameters, please refer to the official
API reference:
https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service-and-log-disabled-reason
@@ -89,6 +117,8 @@
def update_forced_down(self, **kwargs):
"""Set or unset ``forced_down`` flag for the service.
+ ``update_service`` supersedes this API starting with microversion 2.53.
+
For a full list of available parameters, please refer to the official
API reference:
https://developer.openstack.org/api-ref/compute/#update-forced-down
diff --git a/tempest/lib/services/network/agents_client.py b/tempest/lib/services/network/agents_client.py
index a0f832e..5068121 100644
--- a/tempest/lib/services/network/agents_client.py
+++ b/tempest/lib/services/network/agents_client.py
@@ -87,9 +87,11 @@
return self.delete_resource(uri)
def add_dhcp_agent_to_network(self, agent_id, **kwargs):
- # TODO(piyush): Current api-site doesn't contain this API description.
- # After fixing the api-site, we need to fix here also for putting the
- # link to api-site.
- # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526212
+ """Schedule a network to a DHCP agent.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/network/v2/#schedule-a-network-to-a-dhcp-agent
+ """
uri = '/agents/%s/dhcp-networks' % agent_id
return self.create_resource(uri, kwargs, expect_empty_body=True)
diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
index f79bcd8..08e6c94 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -114,12 +114,12 @@
return rest_client.ResponseBody(resp, body)
def update_snapshot_status(self, snapshot_id, **kwargs):
- """Update the specified snapshot's status."""
- # TODO(gmann): api-site doesn't contain doc ref
- # for this API. After fixing the api-site, we need to
- # add the link here.
- # Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
+ """Update status of a snapshot.
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#update-status-of-a-snapshot
+ """
post_body = json.dumps({'os-update_snapshot_status': kwargs})
url = 'snapshots/%s/action' % snapshot_id
resp, body = self.post(url, post_body)
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 739357b..1f0080f 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -77,7 +77,7 @@
def test_write_to_console_special_chars(self):
self._test_write_to_console_helper(
- '\`',
+ r'\`',
'sudo sh -c "echo \\"\\\\\\`\\" >/dev/console"')
self.conn.write_to_console('$')
self._assert_exec_called_with(
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 4a2fff4..be54130 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -59,6 +59,7 @@
self._set_attrs()
self.lock_path = cfg.CONF.oslo_concurrency.lock_path
+
fake_service1_group = cfg.OptGroup(name='fake-service1', title='Fake service1')
FakeService1Group = [
diff --git a/tempest/tests/lib/common/utils/test_data_utils.py b/tempest/tests/lib/common/utils/test_data_utils.py
index b8385b2..a0267d0 100644
--- a/tempest/tests/lib/common/utils/test_data_utils.py
+++ b/tempest/tests/lib/common/utils/test_data_utils.py
@@ -88,7 +88,7 @@
def test_rand_url(self):
actual = data_utils.rand_url()
self.assertIsInstance(actual, str)
- self.assertRegex(actual, "^https://url-[0-9]*\.com$")
+ self.assertRegex(actual, r"^https://url-[0-9]*\.com$")
actual2 = data_utils.rand_url()
self.assertNotEqual(actual, actual2)
diff --git a/tempest/tests/lib/services/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py
index 2dd981c..ba432e3 100644
--- a/tempest/tests/lib/services/compute/test_services_client.py
+++ b/tempest/tests/lib/services/compute/test_services_client.py
@@ -56,6 +56,20 @@
}
}
+ FAKE_UPDATE_SERVICE = {
+ "service": {
+ "id": "e81d66a4-ddd3-4aba-8a84-171d1cb4d339",
+ "binary": "nova-compute",
+ "disabled_reason": "test2",
+ "host": "host1",
+ "state": "down",
+ "status": "disabled",
+ "updated_at": "2012-10-29T13:42:05.000000",
+ "forced_down": False,
+ "zone": "nova"
+ }
+ }
+
def setUp(self):
super(TestServicesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -119,6 +133,28 @@
binary="controller",
disabled_reason='test reason')
+ def _test_update_service(self, bytes_body=False, status=None,
+ disabled_reason=None, forced_down=None):
+ resp_body = copy.deepcopy(self.FAKE_UPDATE_SERVICE)
+ kwargs = {}
+
+ if status is not None:
+ kwargs['status'] = status
+ if disabled_reason is not None:
+ kwargs['disabled_reason'] = disabled_reason
+ if forced_down is not None:
+ kwargs['forced_down'] = forced_down
+
+ resp_body['service'].update(kwargs)
+
+ self.check_service_client_function(
+ self.client.update_service,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ resp_body,
+ bytes_body,
+ service_id=resp_body['service']['id'],
+ **kwargs)
+
def test_log_reason_disabled_service_with_str_body(self):
self._test_log_reason_disabled_service()
@@ -144,3 +180,36 @@
new_callable=mock.PropertyMock(return_value='2.11'))
def test_update_forced_down_with_bytes_body(self, _):
self._test_update_forced_down(bytes_body=True)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.53'))
+ def test_update_service_disable_scheduling_with_str_body(self, _):
+ self._test_update_service(status='disabled',
+ disabled_reason='maintenance')
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.53'))
+ def test_update_service_disable_scheduling_with_bytes_body(self, _):
+ self._test_update_service(status='disabled',
+ disabled_reason='maintenance',
+ bytes_body=True)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.53'))
+ def test_update_service_enable_scheduling_with_str_body(self, _):
+ self._test_update_service(status='enabled')
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.53'))
+ def test_update_service_enable_scheduling_with_bytes_body(self, _):
+ self._test_update_service(status='enabled', bytes_body=True)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.53'))
+ def test_update_service_forced_down_with_str_body(self, _):
+ self._test_update_service(forced_down=True)
+
+ @mock.patch.object(base_compute_client, 'COMPUTE_MICROVERSION',
+ new_callable=mock.PropertyMock(return_value='2.53'))
+ def test_update_service_forced_down_with_bytes_body(self, _):
+ self._test_update_service(forced_down=True, bytes_body=True)
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index 4af7463..1cc9c9a 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -34,7 +34,7 @@
"error on import %s" % ids)
ids = six.text_type(ids).split('\n')
for test_id in ids:
- if re.match('(\w+\.){3}\w+', test_id):
+ if re.match(r'(\w+\.){3}\w+', test_id):
if not test_id.startswith('tempest.'):
parts = test_id.partition('tempest')
fail_id = parts[1] + parts[2]
diff --git a/tools/check_logs.py b/tools/check_logs.py
index b80ccc0..de7e41d 100755
--- a/tools/check_logs.py
+++ b/tools/check_logs.py
@@ -96,7 +96,7 @@
def collect_url_logs(url):
page = urlreq.urlopen(url)
content = page.read()
- logs = re.findall('(screen-[\w-]+\.txt\.gz)</a>', content)
+ logs = re.findall(r'(screen-[\w-]+\.txt\.gz)</a>', content)
return logs
@@ -162,6 +162,7 @@
print("ok")
return 0
+
usage = """
Find non-white-listed log errors in log files from a devstack-gate run.
Log files will be searched for ERROR or CRITICAL messages. If any
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index bbb9019..4eb78fb 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -63,12 +63,13 @@
except HTTPError as err:
if err.code == 404:
return False
- p = re.compile('^tempest\.test_plugins', re.M)
+ p = re.compile(r'^tempest\.test_plugins', re.M)
if p.findall(r.read().decode('utf-8')):
return True
else:
False
+
r = urllib.urlopen(url)
# Gerrit prepends 4 garbage octets to the JSON, in order to counter
# cross-site scripting attacks. Therefore we must discard it so the
diff --git a/tox.ini b/tox.ini
index da0233a..de4f1b7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,7 +19,7 @@
OS_STDOUT_CAPTURE=1
OS_STDERR_CAPTURE=1
OS_TEST_TIMEOUT=160
- PYTHONWARNINGS=default::DeprecationWarning
+ 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
usedevelop = True
install_command = pip install {opts} {packages}