Merge "Support cross AZ backups"
This commit is contained in:
commit
517dcf451a
@ -292,6 +292,7 @@ Request
|
||||
- name: name_optional
|
||||
- snapshot_id: snapshot_id_3
|
||||
- metadata: metadata_9
|
||||
- availability_zone: availability_zone_4
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
@ -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``.
|
||||
|
@ -5,6 +5,7 @@
|
||||
"name": "backup001",
|
||||
"volume_id": "64f5d2fb-d836-4063-b7e2-544d5c1ff607",
|
||||
"incremental": true,
|
||||
"availability_zone": "AZ2",
|
||||
"metadata": null
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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': {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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):
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Cinder backup creation can now (since microversion 3.51) receive the
|
||||
availability zone where the backup should be stored.
|
Loading…
x
Reference in New Issue
Block a user