Adds cinder backup functional tests
Adds new tests to cover cinder backup api as well as the needed json clients. xml client will be submitted after bug #1270589 is fixed. Change-Id: I8d40c58eaf0ccd24abf55a212f1177e61ef0a281 Closes-Bug: 1221335
This commit is contained in:
parent
e45657fef0
commit
74b08ad1e1
|
@ -822,6 +822,9 @@
|
|||
# (boolean value)
|
||||
#multi_backend=false
|
||||
|
||||
# Runs Cinder volumes backup test (boolean value)
|
||||
#backup=true
|
||||
|
||||
# Is the v1 volume API enabled (boolean value)
|
||||
#api_v1=true
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
# 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.base import BaseVolumeV1AdminTest
|
||||
from tempest.common.utils import data_utils
|
||||
from tempest import config
|
||||
from tempest.openstack.common import log as logging
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VolumesBackupsTest(BaseVolumeV1AdminTest):
|
||||
_interface = "json"
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(VolumesBackupsTest, cls).setUpClass()
|
||||
|
||||
if not CONF.volume_feature_enabled.backup:
|
||||
raise cls.skipException("Cinder backup feature disabled")
|
||||
|
||||
cls.volumes_adm_client = cls.os_adm.volumes_client
|
||||
cls.backups_adm_client = cls.os_adm.backups_client
|
||||
cls.volume = cls.create_volume()
|
||||
|
||||
@test.attr(type='smoke')
|
||||
def test_volume_backup_create_get_restore_delete(self):
|
||||
backup_name = data_utils.rand_name('Backup')
|
||||
create_backup = self.backups_adm_client.create_backup
|
||||
resp, backup = create_backup(self.volume['id'],
|
||||
name=backup_name)
|
||||
self.assertEqual(202, resp.status)
|
||||
self.addCleanup(self.backups_adm_client.delete_backup,
|
||||
backup['id'])
|
||||
self.assertEqual(backup['name'], backup_name)
|
||||
self.volumes_adm_client.wait_for_volume_status(self.volume['id'],
|
||||
'available')
|
||||
self.backups_adm_client.wait_for_backup_status(backup['id'],
|
||||
'available')
|
||||
|
||||
resp, backup = self.backups_adm_client.get_backup(backup['id'])
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(backup['name'], backup_name)
|
||||
|
||||
resp, restore = self.backups_adm_client.restore_backup(backup['id'])
|
||||
self.assertEqual(202, resp.status)
|
||||
self.addCleanup(self.volumes_adm_client.delete_volume,
|
||||
restore['volume_id'])
|
||||
self.assertEqual(restore['backup_id'], backup['id'])
|
||||
self.backups_adm_client.wait_for_backup_status(backup['id'],
|
||||
'available')
|
||||
self.volumes_adm_client.wait_for_volume_status(restore['volume_id'],
|
||||
'available')
|
|
@ -106,6 +106,7 @@ class BaseVolumeV1Test(BaseVolumeTest):
|
|||
super(BaseVolumeV1Test, cls).setUpClass()
|
||||
cls.snapshots_client = cls.os.snapshots_client
|
||||
cls.volumes_client = cls.os.volumes_client
|
||||
cls.backups_client = cls.os.backups_client
|
||||
cls.volumes_extension_client = cls.os.volumes_extension_client
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -148,6 +148,7 @@ from tempest.services.volume.json.admin.volume_hosts_client import \
|
|||
VolumeHostsClientJSON
|
||||
from tempest.services.volume.json.admin.volume_types_client import \
|
||||
VolumeTypesClientJSON
|
||||
from tempest.services.volume.json.backups_client import BackupsClientJSON
|
||||
from tempest.services.volume.json.extensions_client import \
|
||||
ExtensionsClientJSON as VolumeExtensionClientJSON
|
||||
from tempest.services.volume.json.snapshots_client import SnapshotsClientJSON
|
||||
|
@ -158,6 +159,7 @@ from tempest.services.volume.xml.admin.volume_hosts_client import \
|
|||
VolumeHostsClientXML
|
||||
from tempest.services.volume.xml.admin.volume_types_client import \
|
||||
VolumeTypesClientXML
|
||||
from tempest.services.volume.xml.backups_client import BackupsClientXML
|
||||
from tempest.services.volume.xml.extensions_client import \
|
||||
ExtensionsClientXML as VolumeExtensionClientXML
|
||||
from tempest.services.volume.xml.snapshots_client import SnapshotsClientXML
|
||||
|
@ -212,6 +214,7 @@ class Manager(object):
|
|||
auth_provider)
|
||||
self.floating_ips_client = FloatingIPsClientXML(
|
||||
auth_provider)
|
||||
self.backups_client = BackupsClientXML(auth_provider)
|
||||
self.snapshots_client = SnapshotsClientXML(auth_provider)
|
||||
self.volumes_client = VolumesClientXML(auth_provider)
|
||||
self.volumes_v2_client = VolumesV2ClientXML(auth_provider)
|
||||
|
@ -277,6 +280,7 @@ class Manager(object):
|
|||
auth_provider)
|
||||
self.floating_ips_client = FloatingIPsClientJSON(
|
||||
auth_provider)
|
||||
self.backups_client = BackupsClientJSON(auth_provider)
|
||||
self.snapshots_client = SnapshotsClientJSON(auth_provider)
|
||||
self.volumes_client = VolumesClientJSON(auth_provider)
|
||||
self.volumes_v2_client = VolumesV2ClientJSON(auth_provider)
|
||||
|
|
|
@ -385,6 +385,9 @@ VolumeFeaturesGroup = [
|
|||
cfg.BoolOpt('multi_backend',
|
||||
default=False,
|
||||
help="Runs Cinder multi-backend test (requires 2 backends)"),
|
||||
cfg.BoolOpt('backup',
|
||||
default=True,
|
||||
help='Runs Cinder volumes backup test'),
|
||||
cfg.ListOpt('api_extensions',
|
||||
default=['all'],
|
||||
help='A list of enabled extensions with a special entry all '
|
||||
|
|
|
@ -100,6 +100,10 @@ class SnapshotBuildErrorException(TempestException):
|
|||
message = "Snapshot %(snapshot_id)s failed to build and is in ERROR status"
|
||||
|
||||
|
||||
class VolumeBackupException(TempestException):
|
||||
message = "Volume backup %(backup_id)s failed and is in ERROR status"
|
||||
|
||||
|
||||
class StackBuildErrorException(TempestException):
|
||||
message = ("Stack %(stack_identifier)s is in %(stack_status)s status "
|
||||
"due to '%(stack_status_reason)s'")
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
# 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 json
|
||||
import time
|
||||
|
||||
from tempest.common import rest_client
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BackupsClientJSON(rest_client.RestClient):
|
||||
"""
|
||||
Client class to send CRUD Volume backup API requests to a Cinder endpoint
|
||||
"""
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(BackupsClientJSON, self).__init__(auth_provider)
|
||||
self.service = CONF.volume.catalog_type
|
||||
self.build_interval = CONF.volume.build_interval
|
||||
self.build_timeout = CONF.volume.build_timeout
|
||||
|
||||
def create_backup(self, volume_id, container=None, name=None,
|
||||
description=None):
|
||||
"""Creates a backup of volume."""
|
||||
post_body = {'volume_id': volume_id}
|
||||
if container:
|
||||
post_body['container'] = container
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
post_body = json.dumps({'backup': post_body})
|
||||
resp, body = self.post('backups', post_body)
|
||||
body = json.loads(body)
|
||||
return resp, body['backup']
|
||||
|
||||
def restore_backup(self, backup_id, volume_id=None):
|
||||
"""Restore volume from backup."""
|
||||
post_body = {'volume_id': volume_id}
|
||||
post_body = json.dumps({'restore': post_body})
|
||||
resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
|
||||
body = json.loads(body)
|
||||
return resp, body['restore']
|
||||
|
||||
def delete_backup(self, backup_id):
|
||||
"""Delete a backup of volume."""
|
||||
resp, body = self.delete('backups/%s' % (str(backup_id)))
|
||||
return resp, body
|
||||
|
||||
def get_backup(self, backup_id):
|
||||
"""Returns the details of a single backup."""
|
||||
url = "backups/%s" % str(backup_id)
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
return resp, body['backup']
|
||||
|
||||
def wait_for_backup_status(self, backup_id, status):
|
||||
"""Waits for a Backup to reach a given status."""
|
||||
resp, body = self.get_backup(backup_id)
|
||||
backup_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while backup_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
resp, body = self.get_backup(backup_id)
|
||||
backup_status = body['status']
|
||||
if backup_status == 'error':
|
||||
raise exceptions.VolumeBackupException(backup_id=backup_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Volume backup %s failed to reach %s status within '
|
||||
'the required time (%s s).' %
|
||||
(backup_id, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
|
@ -0,0 +1,25 @@
|
|||
# Copyright 2014 OpenStack Foundation
|
||||
# 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.common.rest_client import RestClientXML
|
||||
|
||||
|
||||
class BackupsClientXML(RestClientXML):
|
||||
"""
|
||||
Client class to send CRUD Volume Backup API requests to a Cinder endpoint
|
||||
"""
|
||||
|
||||
#TODO(gfidente): XML client isn't yet implemented because of bug 1270589
|
||||
pass
|
Loading…
Reference in New Issue