Merge "Support cross AZ backups"

This commit is contained in:
Zuul 2018-03-18 01:26:19 +00:00 committed by Gerrit Code Review
commit 517dcf451a
12 changed files with 82 additions and 8 deletions

View File

@ -292,6 +292,7 @@ Request
- name: name_optional
- snapshot_id: snapshot_id_3
- metadata: metadata_9
- availability_zone: availability_zone_4
Request Example
---------------

View File

@ -519,6 +519,13 @@ availability_zone_3:
in: body
required: true
type: string
availability_zone_4:
description: |
The backup availability zone key value pair.
in: body
required: false
type: string
min_version: 3.51
backend_id:
description: |
ID of backend to failover to. Default is ``None``.

View File

@ -5,6 +5,7 @@
"name": "backup001",
"volume_id": "64f5d2fb-d836-4063-b7e2-544d5c1ff607",
"incremental": true,
"availability_zone": "AZ2",
"metadata": null
}
}

View File

@ -144,7 +144,8 @@ class BackupsController(wsgi.Controller):
# - maybe also do validation of swift container name
@wsgi.response(http_client.ACCEPTED)
@validation.schema(backup.create, '2.0', '3.42')
@validation.schema(backup.create_backup_v343, '3.43')
@validation.schema(backup.create_backup_v343, '3.43', '3.50')
@validation.schema(backup.create_backup_v351, mv.BACKUP_AZ)
def create(self, req, body):
"""Create a new backup."""
LOG.debug('Creating new backup %s', body)
@ -166,16 +167,24 @@ class BackupsController(wsgi.Controller):
snapshot_id = backup.get('snapshot_id', None)
metadata = backup.get('metadata', None) if req_version.matches(
mv.BACKUP_METADATA) else None
if req_version.matches(mv.BACKUP_AZ):
availability_zone = backup.get('availability_zone', None)
else:
availability_zone = None
az_text = ' in az %s' % availability_zone if availability_zone else ''
LOG.info("Creating backup of volume %(volume_id)s in container"
" %(container)s",
{'volume_id': volume_id, 'container': container},
" %(container)s%(az)s",
{'volume_id': volume_id, 'container': container,
'az': az_text},
context=context)
try:
new_backup = self.backup_api.create(context, name, description,
volume_id, container,
incremental, None, force,
snapshot_id, metadata)
incremental, availability_zone,
force, snapshot_id, metadata)
except (exception.InvalidVolume,
exception.InvalidSnapshot,
exception.InvalidVolumeMetadata,

View File

@ -139,6 +139,8 @@ BACKEND_STATE_REPORT = '3.49'
MULTIATTACH_VOLUMES = '3.50'
BACKUP_AZ = '3.51'
def get_mv_header(version):
"""Gets a formatted HTTP microversion header.

View File

@ -114,6 +114,7 @@ REST_API_VERSION_HISTORY = """
* 3.48 - Add ``shared_targets`` and ``service_uuid`` fields to volume.
* 3.49 - Support report backend storage state in service list.
* 3.50 - Add multiattach capability
* 3.51 - Add support for cross AZ backups.
"""
# The minimum and maximum versions of the API supported
@ -121,7 +122,7 @@ REST_API_VERSION_HISTORY = """
# minimum version of the API supported.
# Explicitly using /v2 endpoints will still work
_MIN_API_VERSION = "3.0"
_MAX_API_VERSION = "3.50"
_MAX_API_VERSION = "3.51"
_LEGACY_API_VERSION2 = "2.0"
UPDATED = "2017-09-19T20:18:14Z"

View File

@ -400,3 +400,7 @@ Support report backend storage state in service list.
Services supporting this microversion are capable of volume multiattach.
This version does not need to be requested when creating the volume, but can
be used as a way to query if the capability exists in the Cinder service.
3.51
----
Add support for cross AZ backups.

View File

@ -51,6 +51,11 @@ create_backup_v343['properties']['backup']['properties'][
'metadata'] = parameter_types.metadata_allows_null
create_backup_v351 = copy.deepcopy(create_backup_v343)
create_backup_v351['properties']['backup']['properties'][
'availability_zone'] = parameter_types.nullable_string
update = {
'type': 'object',
'properties': {

View File

@ -185,3 +185,8 @@ backup_url = {'type': 'string', 'minLength': 1, 'format': 'base64'}
backup_service = {'type': 'string', 'minLength': 0, 'maxLength': 255}
nullable_string = {
'type': ('string', 'null'), 'minLength': 0, 'maxLength': 255
}

View File

@ -227,8 +227,9 @@ class API(base.Base):
previous_status = volume['status']
volume_host = volume_utils.extract_host(volume.host, 'host')
host = self._get_available_backup_service_host(
volume_host, volume.availability_zone)
availability_zone = availability_zone or volume.availability_zone
host = self._get_available_backup_service_host(volume_host,
availability_zone)
# Reserve a quota before setting volume status and backup status
try:
@ -307,6 +308,7 @@ class API(base.Base):
'parent_id': parent_id,
'size': volume['size'],
'host': host,
'availability_zone': availability_zone,
'snapshot_id': snapshot_id,
'data_timestamp': data_timestamp,
'metadata': metadata or {}

View File

@ -595,6 +595,38 @@ class BackupsAPITestCase(test.TestCase):
volume.destroy()
@mock.patch('cinder.objects.Service.is_up', mock.Mock(return_value=True))
@mock.patch('cinder.db.service_get_all')
def test_create_backup_with_availability_zone(self, _mock_service_get_all):
vol_az = 'az1'
backup_svc_az = 'az2'
_mock_service_get_all.return_value = [
{'availability_zone': backup_svc_az, 'host': 'testhost',
'disabled': 0, 'updated_at': timeutils.utcnow(),
'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}]
volume = utils.create_volume(self.context, availability_zone=vol_az,
size=1)
# Create a backup with metadata
body = {'backup': {'name': 'nightly001',
'volume_id': volume.id,
'container': 'nightlybackups',
'availability_zone': backup_svc_az}}
req = webob.Request.blank('/v3/%s/backups' % fake.PROJECT_ID)
req.method = 'POST'
req.headers = mv.get_mv_header(mv.BACKUP_AZ)
req.headers['Content-Type'] = 'application/json'
req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(fakes.wsgi_app(
fake_auth_context=self.user_context))
self.assertEqual(202, res.status_code)
res_dict = jsonutils.loads(res.body)
backup = objects.Backup.get_by_id(self.context,
res_dict['backup']['id'])
self.assertEqual(backup_svc_az, backup.availability_zone)
@mock.patch('cinder.db.service_get_all')
def test_create_backup_inuse_no_force(self,
_mock_service_get_all):

View File

@ -0,0 +1,5 @@
---
features:
- |
Cinder backup creation can now (since microversion 3.51) receive the
availability zone where the backup should be stored.