Add tests for Cinder user messages v3 API
This commit adds basic tests for the list/show/delete v3 APIs for Cinder's new user messages. Depends-On: Id8a4a700c1159be24b15056f401a2ea77804d0a0 Depends-On: I97caa6bfababf7d1cc714296ae66f77d22bf24ab Depends-On: I80c6a0c46c667291c6f7fe2a036717504c110314 Change-Id: I3d9b3fe288333721bf3b2c6c988949f2f253bfcc
This commit is contained in:
30
tempest/api/volume/api_microversion_fixture.py
Normal file
30
tempest/api/volume/api_microversion_fixture.py
Normal file
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# 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 fixtures
|
||||
|
||||
from tempest.services.volume.base import base_v3_client
|
||||
|
||||
|
||||
class APIMicroversionFixture(fixtures.Fixture):
|
||||
|
||||
def __init__(self, volume_microversion):
|
||||
self.volume_microversion = volume_microversion
|
||||
|
||||
def _setUp(self):
|
||||
super(APIMicroversionFixture, self)._setUp()
|
||||
base_v3_client.VOLUME_MICROVERSION = self.volume_microversion
|
||||
self.addCleanup(self._reset_volume_microversion)
|
||||
|
||||
def _reset_volume_microversion(self):
|
||||
base_v3_client.VOLUME_MICROVERSION = None
|
||||
@@ -45,6 +45,10 @@ class BaseVolumeTest(tempest.test.BaseTestCase):
|
||||
if not CONF.volume_feature_enabled.api_v2:
|
||||
msg = "Volume API v2 is disabled"
|
||||
raise cls.skipException(msg)
|
||||
elif cls._api_version == 3:
|
||||
if not CONF.volume_feature_enabled.api_v3:
|
||||
msg = "Volume API v3 is disabled"
|
||||
raise cls.skipException(msg)
|
||||
else:
|
||||
msg = ("Invalid Cinder API version (%s)" % cls._api_version)
|
||||
raise exceptions.InvalidConfiguration(message=msg)
|
||||
|
||||
0
tempest/api/volume/v3/__init__.py
Normal file
0
tempest/api/volume/v3/__init__.py
Normal file
0
tempest/api/volume/v3/admin/__init__.py
Normal file
0
tempest/api/volume/v3/admin/__init__.py
Normal file
98
tempest/api/volume/v3/admin/test_user_messages.py
Normal file
98
tempest/api/volume/v3/admin/test_user_messages.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# Copyright 2016 Andrew Kerr
|
||||
# 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.
|
||||
|
||||
from tempest.api.volume.v3 import base
|
||||
from tempest.common.utils import data_utils
|
||||
from tempest.common import waiters
|
||||
from tempest import exceptions
|
||||
from tempest import test
|
||||
|
||||
MESSAGE_KEYS = [
|
||||
'created_at',
|
||||
'event_id',
|
||||
'guaranteed_until',
|
||||
'id',
|
||||
'message_level',
|
||||
'request_id',
|
||||
'resource_type',
|
||||
'resource_uuid',
|
||||
'user_message',
|
||||
'links']
|
||||
|
||||
|
||||
class UserMessagesTest(base.VolumesV3AdminTest):
|
||||
min_microversion = '3.3'
|
||||
max_microversion = 'latest'
|
||||
|
||||
def _delete_volume(self, volume_id):
|
||||
self.volumes_client.delete_volume(volume_id)
|
||||
self.volumes_client.wait_for_resource_deletion(volume_id)
|
||||
|
||||
def _create_user_message(self):
|
||||
"""Trigger a 'no valid host' situation to generate a message."""
|
||||
bad_protocol = data_utils.rand_name('storage_protocol')
|
||||
bad_vendor = data_utils.rand_name('vendor_name')
|
||||
extra_specs = {'storage_protocol': bad_protocol,
|
||||
'vendor_name': bad_vendor}
|
||||
vol_type_name = data_utils.rand_name('volume-type')
|
||||
bogus_type = self.admin_volume_types_client.create_volume_type(
|
||||
name=vol_type_name,
|
||||
extra_specs=extra_specs)['volume_type']
|
||||
self.addCleanup(self.admin_volume_types_client.delete_volume_type,
|
||||
bogus_type['id'])
|
||||
params = {'volume_type': bogus_type['id']}
|
||||
volume = self.volumes_client.create_volume(**params)['volume']
|
||||
self.addCleanup(self._delete_volume, volume['id'])
|
||||
try:
|
||||
waiters.wait_for_volume_status(self.volumes_client, volume['id'],
|
||||
'error')
|
||||
except exceptions.VolumeBuildErrorException:
|
||||
# Error state is expected and desired
|
||||
pass
|
||||
messages = self.messages_client.list_messages()['messages']
|
||||
message_id = None
|
||||
for message in messages:
|
||||
if message['resource_uuid'] == volume['id']:
|
||||
message_id = message['id']
|
||||
break
|
||||
self.assertIsNotNone(message_id, 'No user message generated for '
|
||||
'volume %s' % volume['id'])
|
||||
return message_id
|
||||
|
||||
@test.idempotent_id('50f29e6e-f363-42e1-8ad1-f67ae7fd4d5a')
|
||||
def test_list_messages(self):
|
||||
self._create_user_message()
|
||||
messages = self.messages_client.list_messages()['messages']
|
||||
self.assertIsInstance(messages, list)
|
||||
for message in messages:
|
||||
for key in MESSAGE_KEYS:
|
||||
self.assertIn(key, message.keys(),
|
||||
'Missing expected key %s' % key)
|
||||
|
||||
@test.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)
|
||||
|
||||
@test.idempotent_id('c6eb6901-cdcc-490f-b735-4fe251842aed')
|
||||
def test_delete_message(self):
|
||||
message_id = self._create_user_message()
|
||||
self.messages_client.delete_message(message_id)
|
||||
self.messages_client.wait_for_resource_deletion(message_id)
|
||||
64
tempest/api/volume/v3/base.py
Normal file
64
tempest/api/volume/v3/base.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# Copyright 2016 Andrew Kerr
|
||||
# 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.
|
||||
from tempest.api.volume import api_microversion_fixture
|
||||
from tempest.api.volume import base
|
||||
from tempest import config
|
||||
from tempest.lib.common import api_version_utils
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class VolumesV3Test(api_version_utils.BaseMicroversionTest,
|
||||
base.BaseVolumeTest):
|
||||
"""Base test case class for all v3 Cinder API tests."""
|
||||
|
||||
_api_version = 3
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(VolumesV3Test, cls).skip_checks()
|
||||
api_version_utils.check_skip_with_microversion(
|
||||
cls.min_microversion, cls.max_microversion,
|
||||
CONF.volume.min_microversion, CONF.volume.max_microversion)
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(VolumesV3Test, cls).resource_setup()
|
||||
cls.request_microversion = (
|
||||
api_version_utils.select_request_microversion(
|
||||
cls.min_microversion,
|
||||
CONF.volume.min_microversion))
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(VolumesV3Test, cls).setup_clients()
|
||||
cls.messages_client = cls.os.volume_messages_client
|
||||
|
||||
def setUp(self):
|
||||
super(VolumesV3Test, self).setUp()
|
||||
self.useFixture(api_microversion_fixture.APIMicroversionFixture(
|
||||
self.request_microversion))
|
||||
|
||||
|
||||
class VolumesV3AdminTest(VolumesV3Test):
|
||||
"""Base test case class for all v3 Volume Admin API tests."""
|
||||
|
||||
credentials = ['primary', 'admin']
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(VolumesV3AdminTest, cls).setup_clients()
|
||||
cls.admin_messages_client = cls.os_adm.volume_messages_client
|
||||
cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
|
||||
@@ -183,6 +183,7 @@ from tempest.services.volume.v2.json.snapshots_client import \
|
||||
SnapshotsClient as SnapshotsV2Client
|
||||
from tempest.services.volume.v2.json.volumes_client import \
|
||||
VolumesClient as VolumesV2Client
|
||||
from tempest.services.volume.v3.json.messages_client import MessagesClient
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
@@ -508,6 +509,8 @@ class Manager(manager.Manager):
|
||||
self.volumes_v2_client = VolumesV2Client(
|
||||
self.auth_provider, default_volume_size=CONF.volume.volume_size,
|
||||
**params)
|
||||
self.volume_messages_client = MessagesClient(self.auth_provider,
|
||||
**params)
|
||||
self.volume_types_client = VolumeTypesClient(self.auth_provider,
|
||||
**params)
|
||||
self.volume_types_v2_client = VolumeTypesV2Client(
|
||||
|
||||
@@ -682,6 +682,24 @@ VolumeGroup = [
|
||||
cfg.IntOpt('volume_size',
|
||||
default=1,
|
||||
help='Default size in GB for volumes created by volumes tests'),
|
||||
cfg.StrOpt('min_microversion',
|
||||
default=None,
|
||||
help="Lower version of the test target microversion range. "
|
||||
"The format is 'X.Y', where 'X' and 'Y' are int values. "
|
||||
"Tempest selects tests based on the range between "
|
||||
"min_microversion and max_microversion. "
|
||||
"If both values are not specified, Tempest avoids tests "
|
||||
"which require a microversion. Valid values are string "
|
||||
"with format 'X.Y' or string 'latest'",),
|
||||
cfg.StrOpt('max_microversion',
|
||||
default=None,
|
||||
help="Upper version of the test target microversion range. "
|
||||
"The format is 'X.Y', where 'X' and 'Y' are int values. "
|
||||
"Tempest selects tests based on the range between "
|
||||
"min_microversion and max_microversion. "
|
||||
"If both values are not specified, Tempest avoids tests "
|
||||
"which require a microversion. Valid values are string "
|
||||
"with format 'X.Y' or string 'latest'",),
|
||||
]
|
||||
|
||||
volume_feature_group = cfg.OptGroup(name='volume-feature-enabled',
|
||||
@@ -711,6 +729,9 @@ VolumeFeaturesGroup = [
|
||||
cfg.BoolOpt('api_v2',
|
||||
default=True,
|
||||
help="Is the v2 volume API enabled"),
|
||||
cfg.BoolOpt('api_v3',
|
||||
default=False,
|
||||
help="Is the v3 volume API enabled"),
|
||||
cfg.BoolOpt('bootable',
|
||||
default=True,
|
||||
help='Update bootable status of a volume '
|
||||
|
||||
46
tempest/services/volume/base/base_v3_client.py
Normal file
46
tempest/services/volume/base/base_v3_client.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# Copyright 2016 Andrew Kerr
|
||||
# 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.
|
||||
|
||||
from tempest.lib.common import api_version_utils
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
VOLUME_MICROVERSION = None
|
||||
|
||||
|
||||
class BaseV3Client(rest_client.RestClient):
|
||||
"""Base class to handle Cinder v3 client microversion support."""
|
||||
api_version = 'v3'
|
||||
api_microversion_header_name = 'Openstack-Api-Version'
|
||||
|
||||
def get_headers(self, accept_type=None, send_type=None):
|
||||
headers = super(BaseV3Client, self).get_headers(
|
||||
accept_type=accept_type, send_type=send_type)
|
||||
if VOLUME_MICROVERSION:
|
||||
headers[self.api_microversion_header_name] = ('volume %s' %
|
||||
VOLUME_MICROVERSION)
|
||||
return headers
|
||||
|
||||
def request(self, method, url, extra_headers=False, headers=None,
|
||||
body=None, chunked=False):
|
||||
|
||||
resp, resp_body = super(BaseV3Client, self).request(
|
||||
method, url, extra_headers, headers, body, chunked)
|
||||
if (VOLUME_MICROVERSION and
|
||||
VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
|
||||
api_version_utils.assert_version_header_matches_request(
|
||||
self.api_microversion_header_name,
|
||||
'volume %s' % VOLUME_MICROVERSION,
|
||||
resp)
|
||||
return resp, resp_body
|
||||
0
tempest/services/volume/v3/__init__.py
Normal file
0
tempest/services/volume/v3/__init__.py
Normal file
0
tempest/services/volume/v3/json/__init__.py
Normal file
0
tempest/services/volume/v3/json/__init__.py
Normal file
59
tempest/services/volume/v3/json/messages_client.py
Normal file
59
tempest/services/volume/v3/json/messages_client.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# Copyright 2016 Andrew Kerr
|
||||
# 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.
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from tempest.lib.common import rest_client
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest.services.volume.base import base_v3_client
|
||||
|
||||
|
||||
class MessagesClient(base_v3_client.BaseV3Client):
|
||||
"""Client class to send user messages API requests."""
|
||||
|
||||
def show_message(self, message_id):
|
||||
"""Show details for a single message."""
|
||||
url = 'messages/%s' % str(message_id)
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
self.expected_success(200, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_messages(self):
|
||||
"""List all messages."""
|
||||
url = 'messages'
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
self.expected_success(200, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_message(self, message_id):
|
||||
"""Delete a single message."""
|
||||
url = 'messages/%s' % str(message_id)
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(204, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def is_resource_deleted(self, id):
|
||||
try:
|
||||
self.show_message(id)
|
||||
except lib_exc.NotFound:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def resource_type(self):
|
||||
"""Returns the primary type of resource this client works with."""
|
||||
return 'message'
|
||||
Reference in New Issue
Block a user