Add backup update function (microversion)
Add update interface so that users can update name and description of a backup. This new API endpoint is added in microversion 3.9. APIImpact Add PUT to /backups/<id>. DocImpact Change-Id: If592b53c7e1dcdc36dbcaa89425b8e44a51684c3
This commit is contained in:
parent
eaac65bb48
commit
c5ebe48b8e
@ -56,6 +56,7 @@ REST_API_VERSION_HISTORY = """
|
|||||||
group in consisgroup-update operation.
|
group in consisgroup-update operation.
|
||||||
* 3.7 - Add cluster API and cluster_name field to service list API
|
* 3.7 - Add cluster API and cluster_name field to service list API
|
||||||
* 3.8 - Adds resources from volume_manage and snapshot_manage extensions.
|
* 3.8 - Adds resources from volume_manage and snapshot_manage extensions.
|
||||||
|
* 3.9 - Add backup update interface.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ REST_API_VERSION_HISTORY = """
|
|||||||
# minimum version of the API supported.
|
# minimum version of the API supported.
|
||||||
# Explicitly using /v1 or /v2 enpoints will still work
|
# Explicitly using /v1 or /v2 enpoints will still work
|
||||||
_MIN_API_VERSION = "3.0"
|
_MIN_API_VERSION = "3.0"
|
||||||
_MAX_API_VERSION = "3.8"
|
_MAX_API_VERSION = "3.9"
|
||||||
_LEGACY_API_VERSION1 = "1.0"
|
_LEGACY_API_VERSION1 = "1.0"
|
||||||
_LEGACY_API_VERSION2 = "2.0"
|
_LEGACY_API_VERSION2 = "2.0"
|
||||||
|
|
||||||
|
@ -152,3 +152,16 @@ user documentation.
|
|||||||
Adds the following resources that were previously in extensions:
|
Adds the following resources that were previously in extensions:
|
||||||
- os-volume-manage => /v3/<project_id>/manageable_volumes
|
- os-volume-manage => /v3/<project_id>/manageable_volumes
|
||||||
- os-snapshot-manage => /v3/<project_id>/manageable_snapshots
|
- os-snapshot-manage => /v3/<project_id>/manageable_snapshots
|
||||||
|
|
||||||
|
3.9
|
||||||
|
---
|
||||||
|
Added backup update interface to change name and description.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"backup": {
|
||||||
|
"id": "backup_id",
|
||||||
|
"name": "backup_name",
|
||||||
|
"links": "backup_link",
|
||||||
|
}
|
||||||
|
56
cinder/api/v3/backups.py
Normal file
56
cinder/api/v3/backups.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright (c) 2016 Intel, Inc.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""The backups V3 api."""
|
||||||
|
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from cinder.api.contrib import backups as backups_v2
|
||||||
|
from cinder.api.openstack import wsgi
|
||||||
|
from cinder.i18n import _
|
||||||
|
|
||||||
|
BACKUP_UPDATE_MICRO_VERSION = '3.9'
|
||||||
|
|
||||||
|
|
||||||
|
class BackupsController(backups_v2.BackupsController):
|
||||||
|
"""The backups API controller for the Openstack API V3."""
|
||||||
|
|
||||||
|
@wsgi.Controller.api_version(BACKUP_UPDATE_MICRO_VERSION)
|
||||||
|
def update(self, req, id, body):
|
||||||
|
"""Update a backup."""
|
||||||
|
context = req.environ['cinder.context']
|
||||||
|
self.assert_valid_body(body, 'backup')
|
||||||
|
|
||||||
|
backup_update = body['backup']
|
||||||
|
|
||||||
|
self.validate_name_and_description(backup_update)
|
||||||
|
update_dict = {}
|
||||||
|
if 'name' in backup_update:
|
||||||
|
update_dict['display_name'] = backup_update.pop('name')
|
||||||
|
if 'description' in backup_update:
|
||||||
|
update_dict['display_description'] = (
|
||||||
|
backup_update.pop('description'))
|
||||||
|
# Check no unsupported fields.
|
||||||
|
if backup_update:
|
||||||
|
msg = _("Unsupported fields %s.") % (", ".join(backup_update))
|
||||||
|
raise exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
new_backup = self.backup_api.update(context, id, update_dict)
|
||||||
|
|
||||||
|
return self._view_builder.summary(req, new_backup)
|
||||||
|
|
||||||
|
|
||||||
|
def create_resource():
|
||||||
|
return wsgi.Resource(BackupsController())
|
@ -26,6 +26,7 @@ from cinder.api.v2 import snapshot_metadata
|
|||||||
from cinder.api.v2 import snapshots
|
from cinder.api.v2 import snapshots
|
||||||
from cinder.api.v2 import types
|
from cinder.api.v2 import types
|
||||||
from cinder.api.v2 import volume_metadata
|
from cinder.api.v2 import volume_metadata
|
||||||
|
from cinder.api.v3 import backups
|
||||||
from cinder.api.v3 import clusters
|
from cinder.api.v3 import clusters
|
||||||
from cinder.api.v3 import consistencygroups
|
from cinder.api.v3 import consistencygroups
|
||||||
from cinder.api.v3 import messages
|
from cinder.api.v3 import messages
|
||||||
@ -125,3 +126,9 @@ class APIRouter(cinder.api.openstack.APIRouter):
|
|||||||
mapper.resource("manageable_snapshot", "manageable_snapshots",
|
mapper.resource("manageable_snapshot", "manageable_snapshots",
|
||||||
controller=self.resources['manageable_snapshots'],
|
controller=self.resources['manageable_snapshots'],
|
||||||
collection={'detail': 'GET'})
|
collection={'detail': 'GET'})
|
||||||
|
|
||||||
|
self.resources['backups'] = (
|
||||||
|
backups.create_resource())
|
||||||
|
mapper.resource("backup", "backups",
|
||||||
|
controller=self.resources['backups'],
|
||||||
|
collection={'detail': 'GET'})
|
||||||
|
@ -589,3 +589,10 @@ class API(base.Base):
|
|||||||
hosts)
|
hosts)
|
||||||
|
|
||||||
return backup
|
return backup
|
||||||
|
|
||||||
|
def update(self, context, backup_id, fields):
|
||||||
|
check_policy(context, 'update')
|
||||||
|
backup = self.get(context, backup_id)
|
||||||
|
backup.update(fields)
|
||||||
|
backup.save()
|
||||||
|
return backup
|
||||||
|
104
cinder/tests/unit/api/v3/test_backups.py
Normal file
104
cinder/tests/unit/api/v3/test_backups.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# Copyright (c) 2016 Intel, Inc.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""The backups V3 api."""
|
||||||
|
|
||||||
|
import webob
|
||||||
|
|
||||||
|
from cinder.api.openstack import api_version_request as api_version
|
||||||
|
from cinder.api.v3 import backups
|
||||||
|
import cinder.backup
|
||||||
|
from cinder import context
|
||||||
|
from cinder import exception
|
||||||
|
from cinder.objects import fields
|
||||||
|
from cinder import test
|
||||||
|
from cinder.tests.unit.api import fakes
|
||||||
|
from cinder.tests.unit import fake_constants as fake
|
||||||
|
from cinder.tests.unit import utils as test_utils
|
||||||
|
|
||||||
|
|
||||||
|
class BackupsControllerAPITestCase(test.TestCase):
|
||||||
|
"""Test cases for backups API."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(BackupsControllerAPITestCase, self).setUp()
|
||||||
|
self.backup_api = cinder.backup.API()
|
||||||
|
self.ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID,
|
||||||
|
auth_token=True,
|
||||||
|
is_admin=True)
|
||||||
|
self.controller = backups.BackupsController()
|
||||||
|
|
||||||
|
def _fake_update_request(self, backup_id, version='3.9'):
|
||||||
|
req = fakes.HTTPRequest.blank('/v3/%s/backups/%s/update' %
|
||||||
|
(fake.PROJECT_ID, backup_id))
|
||||||
|
req.environ['cinder.context'].is_admin = True
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.headers['OpenStack-API-Version'] = 'volume ' + version
|
||||||
|
req.api_version_request = api_version.APIVersionRequest(version)
|
||||||
|
return req
|
||||||
|
|
||||||
|
def test_update_wrong_version(self):
|
||||||
|
req = self._fake_update_request(fake.BACKUP_ID, version='3.6')
|
||||||
|
body = {"backup": {"name": "Updated Test Name", }}
|
||||||
|
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||||
|
self.controller.update, req, fake.BACKUP_ID,
|
||||||
|
body)
|
||||||
|
|
||||||
|
def test_backup_update_with_no_body(self):
|
||||||
|
# omit body from the request
|
||||||
|
req = self._fake_update_request(fake.BACKUP_ID)
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.update,
|
||||||
|
req, fake.BACKUP_ID, None)
|
||||||
|
|
||||||
|
def test_backup_update_with_unsupported_field(self):
|
||||||
|
req = self._fake_update_request(fake.BACKUP_ID)
|
||||||
|
body = {"backup": {"id": fake.BACKUP2_ID,
|
||||||
|
"description": "", }}
|
||||||
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
|
self.controller.update,
|
||||||
|
req, fake.BACKUP_ID, body)
|
||||||
|
|
||||||
|
def test_backup_update_with_backup_not_found(self):
|
||||||
|
req = self._fake_update_request(fake.BACKUP_ID)
|
||||||
|
updates = {
|
||||||
|
"name": "Updated Test Name",
|
||||||
|
"description": "Updated Test description.",
|
||||||
|
}
|
||||||
|
body = {"backup": updates}
|
||||||
|
self.assertRaises(exception.NotFound,
|
||||||
|
self.controller.update,
|
||||||
|
req, fake.BACKUP_ID, body)
|
||||||
|
|
||||||
|
def test_backup_update(self):
|
||||||
|
backup = test_utils.create_backup(
|
||||||
|
self.ctxt,
|
||||||
|
status=fields.BackupStatus.AVAILABLE)
|
||||||
|
req = self._fake_update_request(fake.BACKUP_ID)
|
||||||
|
new_name = "updated_test_name"
|
||||||
|
new_description = "Updated Test description."
|
||||||
|
updates = {
|
||||||
|
"name": new_name,
|
||||||
|
"description": new_description,
|
||||||
|
}
|
||||||
|
body = {"backup": updates}
|
||||||
|
self.controller.update(req,
|
||||||
|
backup.id,
|
||||||
|
body)
|
||||||
|
|
||||||
|
backup.refresh()
|
||||||
|
self.assertEqual(new_name, backup.display_name)
|
||||||
|
self.assertEqual(new_description,
|
||||||
|
backup.display_description)
|
@ -97,6 +97,7 @@
|
|||||||
"backup:restore": "",
|
"backup:restore": "",
|
||||||
"backup:backup-import": "rule:admin_api",
|
"backup:backup-import": "rule:admin_api",
|
||||||
"backup:backup-export": "rule:admin_api",
|
"backup:backup-export": "rule:admin_api",
|
||||||
|
"backup:update": "rule:admin_or_owner",
|
||||||
|
|
||||||
"volume_extension:replication:promote": "rule:admin_api",
|
"volume_extension:replication:promote": "rule:admin_api",
|
||||||
"volume_extension:replication:reenable": "rule:admin_api",
|
"volume_extension:replication:reenable": "rule:admin_api",
|
||||||
|
@ -5935,9 +5935,8 @@ class GenericVolumeDriverTestCase(DriverTestCase):
|
|||||||
vol = tests_utils.create_volume(self.context)
|
vol = tests_utils.create_volume(self.context)
|
||||||
self.context.user_id = fake.USER_ID
|
self.context.user_id = fake.USER_ID
|
||||||
self.context.project_id = fake.PROJECT_ID
|
self.context.project_id = fake.PROJECT_ID
|
||||||
backup = tests_utils.create_backup(self.context,
|
backup_obj = tests_utils.create_backup(self.context,
|
||||||
vol['id'])
|
vol['id'])
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
|
||||||
properties = {}
|
properties = {}
|
||||||
attach_info = {'device': {'path': '/dev/null'}}
|
attach_info = {'device': {'path': '/dev/null'}}
|
||||||
backup_service = mock.Mock()
|
backup_service = mock.Mock()
|
||||||
@ -5995,9 +5994,8 @@ class GenericVolumeDriverTestCase(DriverTestCase):
|
|||||||
temp_vol = tests_utils.create_volume(self.context)
|
temp_vol = tests_utils.create_volume(self.context)
|
||||||
self.context.user_id = fake.USER_ID
|
self.context.user_id = fake.USER_ID
|
||||||
self.context.project_id = fake.PROJECT_ID
|
self.context.project_id = fake.PROJECT_ID
|
||||||
backup = tests_utils.create_backup(self.context,
|
backup_obj = tests_utils.create_backup(self.context,
|
||||||
vol['id'])
|
vol['id'])
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
|
||||||
properties = {}
|
properties = {}
|
||||||
attach_info = {'device': {'path': '/dev/null'}}
|
attach_info = {'device': {'path': '/dev/null'}}
|
||||||
backup_service = mock.Mock()
|
backup_service = mock.Mock()
|
||||||
@ -6088,16 +6086,15 @@ class GenericVolumeDriverTestCase(DriverTestCase):
|
|||||||
vol = tests_utils.create_volume(self.context)
|
vol = tests_utils.create_volume(self.context)
|
||||||
self.context.user_id = fake.USER_ID
|
self.context.user_id = fake.USER_ID
|
||||||
self.context.project_id = fake.PROJECT_ID
|
self.context.project_id = fake.PROJECT_ID
|
||||||
backup = tests_utils.create_backup(self.context,
|
backup_obj = tests_utils.create_backup(self.context,
|
||||||
vol['id'])
|
vol['id'])
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
|
||||||
(backup_device, is_snapshot) = self.volume.driver.get_backup_device(
|
(backup_device, is_snapshot) = self.volume.driver.get_backup_device(
|
||||||
self.context, backup_obj)
|
self.context, backup_obj)
|
||||||
volume = objects.Volume.get_by_id(self.context, vol.id)
|
volume = objects.Volume.get_by_id(self.context, vol.id)
|
||||||
self.assertEqual(volume, backup_device)
|
self.assertEqual(volume, backup_device)
|
||||||
self.assertFalse(is_snapshot)
|
self.assertFalse(is_snapshot)
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
backup_obj.refresh()
|
||||||
self.assertIsNone(backup.temp_volume_id)
|
self.assertIsNone(backup_obj.temp_volume_id)
|
||||||
|
|
||||||
def test_get_backup_device_in_use(self):
|
def test_get_backup_device_in_use(self):
|
||||||
vol = tests_utils.create_volume(self.context,
|
vol = tests_utils.create_volume(self.context,
|
||||||
@ -6106,9 +6103,8 @@ class GenericVolumeDriverTestCase(DriverTestCase):
|
|||||||
temp_vol = tests_utils.create_volume(self.context)
|
temp_vol = tests_utils.create_volume(self.context)
|
||||||
self.context.user_id = fake.USER_ID
|
self.context.user_id = fake.USER_ID
|
||||||
self.context.project_id = fake.PROJECT_ID
|
self.context.project_id = fake.PROJECT_ID
|
||||||
backup = tests_utils.create_backup(self.context,
|
backup_obj = tests_utils.create_backup(self.context,
|
||||||
vol['id'])
|
vol['id'])
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.volume.driver,
|
self.volume.driver,
|
||||||
'_create_temp_cloned_volume') as mock_create_temp:
|
'_create_temp_cloned_volume') as mock_create_temp:
|
||||||
@ -6118,7 +6114,7 @@ class GenericVolumeDriverTestCase(DriverTestCase):
|
|||||||
backup_obj))
|
backup_obj))
|
||||||
self.assertEqual(temp_vol, backup_device)
|
self.assertEqual(temp_vol, backup_device)
|
||||||
self.assertFalse(is_snapshot)
|
self.assertFalse(is_snapshot)
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
backup_obj.refresh()
|
||||||
self.assertEqual(temp_vol.id, backup_obj.temp_volume_id)
|
self.assertEqual(temp_vol.id, backup_obj.temp_volume_id)
|
||||||
|
|
||||||
def test__create_temp_volume_from_snapshot(self):
|
def test__create_temp_volume_from_snapshot(self):
|
||||||
|
@ -208,7 +208,7 @@ def create_cgsnapshot(ctxt,
|
|||||||
|
|
||||||
|
|
||||||
def create_backup(ctxt,
|
def create_backup(ctxt,
|
||||||
volume_id,
|
volume_id=fake.VOLUME_ID,
|
||||||
display_name='test_backup',
|
display_name='test_backup',
|
||||||
display_description='This is a test backup',
|
display_description='This is a test backup',
|
||||||
status=fields.BackupStatus.CREATING,
|
status=fields.BackupStatus.CREATING,
|
||||||
@ -216,27 +216,32 @@ def create_backup(ctxt,
|
|||||||
temp_volume_id=None,
|
temp_volume_id=None,
|
||||||
temp_snapshot_id=None,
|
temp_snapshot_id=None,
|
||||||
snapshot_id=None,
|
snapshot_id=None,
|
||||||
data_timestamp=None):
|
data_timestamp=None,
|
||||||
backup = {}
|
**kwargs):
|
||||||
backup['volume_id'] = volume_id
|
"""Create a backup object."""
|
||||||
backup['user_id'] = ctxt.user_id
|
values = {
|
||||||
backup['project_id'] = ctxt.project_id
|
'user_id': ctxt.user_id or fake.USER_ID,
|
||||||
backup['host'] = socket.gethostname()
|
'project_id': ctxt.project_id or fake.PROJECT_ID,
|
||||||
backup['availability_zone'] = '1'
|
'volume_id': volume_id,
|
||||||
backup['display_name'] = display_name
|
'status': status,
|
||||||
backup['display_description'] = display_description
|
'display_name': display_name,
|
||||||
backup['container'] = 'fake'
|
'display_description': display_description,
|
||||||
backup['status'] = status
|
'container': 'fake',
|
||||||
backup['fail_reason'] = ''
|
'availability_zone': 'fake',
|
||||||
backup['service'] = 'fake'
|
'service': 'fake',
|
||||||
backup['parent_id'] = parent_id
|
'size': 5 * 1024 * 1024,
|
||||||
backup['size'] = 5 * 1024 * 1024
|
'object_count': 22,
|
||||||
backup['object_count'] = 22
|
'host': socket.gethostname(),
|
||||||
backup['temp_volume_id'] = temp_volume_id
|
'parent_id': parent_id,
|
||||||
backup['temp_snapshot_id'] = temp_snapshot_id
|
'temp_volume_id': temp_volume_id,
|
||||||
backup['snapshot_id'] = snapshot_id
|
'temp_snapshot_id': temp_snapshot_id,
|
||||||
backup['data_timestamp'] = data_timestamp
|
'snapshot_id': snapshot_id,
|
||||||
return db.backup_create(ctxt, backup)
|
'data_timestamp': data_timestamp, }
|
||||||
|
|
||||||
|
values.update(kwargs)
|
||||||
|
backup = objects.Backup(ctxt, **values)
|
||||||
|
backup.create()
|
||||||
|
return backup
|
||||||
|
|
||||||
|
|
||||||
def create_message(ctxt,
|
def create_message(ctxt,
|
||||||
|
@ -22,7 +22,6 @@ from oslo_config import cfg
|
|||||||
from cinder.brick.local_dev import lvm as brick_lvm
|
from cinder.brick.local_dev import lvm as brick_lvm
|
||||||
from cinder import db
|
from cinder import db
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder import objects
|
|
||||||
from cinder.objects import fields
|
from cinder.objects import fields
|
||||||
from cinder.tests import fake_driver
|
from cinder.tests import fake_driver
|
||||||
from cinder.tests.unit.brick import fake_lvm
|
from cinder.tests.unit.brick import fake_lvm
|
||||||
@ -145,9 +144,8 @@ class LVMVolumeDriverTestCase(DriverTestCase):
|
|||||||
vol = tests_utils.create_volume(self.context)
|
vol = tests_utils.create_volume(self.context)
|
||||||
self.context.user_id = fake.USER_ID
|
self.context.user_id = fake.USER_ID
|
||||||
self.context.project_id = fake.PROJECT_ID
|
self.context.project_id = fake.PROJECT_ID
|
||||||
backup = tests_utils.create_backup(self.context,
|
backup_obj = tests_utils.create_backup(self.context,
|
||||||
vol['id'])
|
vol['id'])
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
|
||||||
|
|
||||||
properties = {}
|
properties = {}
|
||||||
attach_info = {'device': {'path': '/dev/null'}}
|
attach_info = {'device': {'path': '/dev/null'}}
|
||||||
@ -233,9 +231,8 @@ class LVMVolumeDriverTestCase(DriverTestCase):
|
|||||||
|
|
||||||
mock_volume_get.return_value = vol
|
mock_volume_get.return_value = vol
|
||||||
temp_snapshot = tests_utils.create_snapshot(self.context, vol['id'])
|
temp_snapshot = tests_utils.create_snapshot(self.context, vol['id'])
|
||||||
backup = tests_utils.create_backup(self.context,
|
backup_obj = tests_utils.create_backup(self.context,
|
||||||
vol['id'])
|
vol['id'])
|
||||||
backup_obj = objects.Backup.get_by_id(self.context, backup.id)
|
|
||||||
properties = {}
|
properties = {}
|
||||||
attach_info = {'device': {'path': '/dev/null'}}
|
attach_info = {'device': {'path': '/dev/null'}}
|
||||||
backup_service = mock.Mock()
|
backup_service = mock.Mock()
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
"backup:restore": "rule:admin_or_owner",
|
"backup:restore": "rule:admin_or_owner",
|
||||||
"backup:backup-import": "rule:admin_api",
|
"backup:backup-import": "rule:admin_api",
|
||||||
"backup:backup-export": "rule:admin_api",
|
"backup:backup-export": "rule:admin_api",
|
||||||
|
"backup:update": "rule:admin_or_owner",
|
||||||
|
|
||||||
"snapshot_extension:snapshot_actions:update_snapshot_status": "",
|
"snapshot_extension:snapshot_actions:update_snapshot_status": "",
|
||||||
"snapshot_extension:snapshot_manage": "rule:admin_api",
|
"snapshot_extension:snapshot_manage": "rule:admin_api",
|
||||||
|
3
releasenotes/notes/backup-update-d0b0db6a7b1c2a5b.yaml
Normal file
3
releasenotes/notes/backup-update-d0b0db6a7b1c2a5b.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added REST API to update backup name and description.
|
Loading…
x
Reference in New Issue
Block a user