Add volume backup import/export commands

This adds commands to import and export volume backup records so they
can be imported and restored on other Cinder instances or to the
original instance if the service or database has been lost and had to be
rebuilt.

I know this is a commonly used process by some users, so it would be
good to have this functionality in osc so they do not have to switch
clients.

More details about the export and import process can be found here:

https://docs.openstack.org/cinder/latest/admin/blockstorage-volume-backups-export-import.html

Change-Id: Ic95f87b36a416a2b50cb2193fd5759ab59336975
Signed-off-by: Sean McGinnis <sean.mcginnis@gmail.com>
This commit is contained in:
Sean McGinnis 2018-10-23 10:36:34 -05:00
parent 097b45686e
commit b90c780d2b
No known key found for this signature in database
GPG Key ID: CE7EE4BFAF8D70C8
8 changed files with 250 additions and 197 deletions

View File

@ -2,200 +2,8 @@
volume backup
=============
Block Storage v1, v2
Volume v1, v2
volume backup create
--------------------
.. autoprogram-cliff:: openstack.volume.v2
:command: volume backup *
Create new volume backup
.. program:: volume backup create
.. code:: bash
openstack volume backup create
[--container <container>]
[--name <name>]
[--description <description>]
[--snapshot <snapshot>]
[--force]
[--incremental]
<volume>
.. option:: --container <container>
Optional backup container name
.. option:: --name <name>
Name of the backup
.. option:: --description <description>
Description of the backup
.. option:: --snapshot <snapshot>
Snapshot to backup (name or ID)
*Volume version 2 only*
.. option:: --force
Allow to back up an in-use volume
*Volume version 2 only*
.. option:: --incremental
Perform an incremental backup
*Volume version 2 only*
.. _volume_backup_create-backup:
.. describe:: <volume>
Volume to backup (name or ID)
volume backup delete
--------------------
Delete volume backup(s)
.. program:: volume backup delete
.. code:: bash
openstack volume backup delete
[--force]
<backup> [<backup> ...]
.. option:: --force
Allow delete in state other than error or available
*Volume version 2 only*
.. _volume_backup_delete-backup:
.. describe:: <backup>
Backup(s) to delete (name or ID)
volume backup list
------------------
List volume backups
.. program:: volume backup list
.. code:: bash
openstack volume backup list
[--long]
[--name <name>]
[--status <status>]
[--volume <volume>]
[--marker <volume-backup>]
[--limit <num-backups>]
[--all-projects]
.. _volume_backup_list-backup:
.. option:: --long
List additional fields in output
.. option:: --name <name>
Filters results by the backup name
.. option:: --status <status>
Filters results by the backup status
('creating', 'available', 'deleting', 'error', 'restoring' or 'error_restoring')
.. option:: --volume <volume>
Filters results by the volume which they backup (name or ID)"
.. option:: --marker <volume-backup>
The last backup of the previous page (name or ID)
*Volume version 2 only*
.. option:: --limit <num-backups>
Maximum number of backups to display
*Volume version 2 only*
.. option:: --all-projects
Include all projects (admin only)
volume backup restore
---------------------
Restore volume backup
.. program:: volume backup restore
.. code:: bash
openstack volume backup restore
<backup>
<volume>
.. _volume_backup_restore-backup:
.. describe:: <backup>
Backup to restore (name or ID)
.. describe:: <volume>
Volume to restore to (name or ID)
volume backup set
-----------------
Set volume backup properties
.. program:: volume backup set
.. code:: bash
openstack volume backup set
[--name <name>]
[--description <description>]
[--state <state>]
<backup>
.. option:: --name <name>
New backup name
.. option:: --description <description>
New backup description
.. option:: --state <state>
New backup state ("available" or "error") (admin only)
(This option simply changes the state of the backup in the database with
no regard to actual status, exercise caution when using)
.. _backup_set-volume-backup:
.. describe:: <backup>
Backup to modify (name or ID)
volume backup show
------------------
Display volume backup details
.. program:: volume backup show
.. code:: bash
openstack volume backup show
<backup>
.. _volume_backup_show-backup:
.. describe:: <backup>
Backup to display (name or ID)

View File

@ -158,6 +158,8 @@ referring to both Compute and Volume quotas.
* ``volume backup``: (**Volume**) backup for volumes
* ``volume backend capability``: (**volume**) volume backend storage capabilities
* ``volume backend pool``: (**volume**) volume backend storage pools
* ``volume backup record``: (**Volume**) volume record that can be imported or exported
* ``volume backend``: (**volume**) volume backend storage
* ``volume host``: (**Volume**) the physical computer for volumes
* ``volume qos``: (**Volume**) quality-of-service (QoS) specification for volumes
* ``volume snapshot``: (**Volume**) a point-in-time copy of a volume

View File

@ -2,8 +2,8 @@ absolute-limits,limits show --absolute,Lists absolute limits for a user.
availability-zone-list,availability zone list --volume,Lists all availability zones.
backup-create,volume backup create,Creates a volume backup.
backup-delete,volume backup delete,Removes a backup.
backup-export,volume backup export,Export backup metadata record.
backup-import,volume backup import,Import backup metadata record.
backup-export,volume backup record export,Export backup metadata record.
backup-import,volume backup record import,Import backup metadata record.
backup-list,volume backup list,Lists all backups.
backup-reset-state,volume backup set --state,Explicitly updates the backup state.
backup-restore,volume backup restore,Restores a backup.

1 absolute-limits limits show --absolute Lists absolute limits for a user.
2 availability-zone-list availability zone list --volume Lists all availability zones.
3 backup-create volume backup create Creates a volume backup.
4 backup-delete volume backup delete Removes a backup.
5 backup-export volume backup export volume backup record export Export backup metadata record.
6 backup-import volume backup import volume backup record import Import backup metadata record.
7 backup-list volume backup list Lists all backups.
8 backup-reset-state volume backup set --state Explicitly updates the backup state.
9 backup-restore volume backup restore Restores a backup.

View File

@ -596,6 +596,35 @@ class FakeBackup(object):
return mock.Mock(side_effect=backups)
@staticmethod
def create_backup_record():
"""Gets a fake backup record for a given backup.
:return: An "exported" backup record.
"""
return {
'backup_service': 'cinder.backup.drivers.swift.SwiftBackupDriver',
'backup_url': 'eyJzdGF0dXMiOiAiYXZh',
}
@staticmethod
def import_backup_record():
"""Creates a fake backup record import response from a backup.
:return: The fake backup object that was encoded.
"""
return {
'backup': {
'id': 'backup.id',
'name': 'backup.name',
'links': [
{'href': 'link1', 'rel': 'self'},
{'href': 'link2', 'rel': 'bookmark'},
],
},
}
class FakeConsistencyGroup(object):
"""Fake one or more consistency group."""

View File

@ -0,0 +1,114 @@
#
# 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 openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
from openstackclient.volume.v2 import backup_record
class TestBackupRecord(volume_fakes.TestVolume):
def setUp(self):
super(TestBackupRecord, self).setUp()
self.backups_mock = self.app.client_manager.volume.backups
self.backups_mock.reset_mock()
class TestBackupRecordExport(TestBackupRecord):
new_backup = volume_fakes.FakeBackup.create_one_backup(
attrs={'volume_id': 'a54708a2-0388-4476-a909-09579f885c25'})
new_record = volume_fakes.FakeBackup.create_backup_record()
def setUp(self):
super(TestBackupRecordExport, self).setUp()
self.backups_mock.export_record.return_value = self.new_record
self.backups_mock.get.return_value = self.new_backup
# Get the command object to mock
self.cmd = backup_record.ExportBackupRecord(self.app, None)
def test_backup_export_table(self):
arglist = [
self.new_backup.name,
]
verifylist = [
("backup", self.new_backup.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
parsed_args.formatter = 'table'
columns, __ = self.cmd.take_action(parsed_args)
self.backups_mock.export_record.assert_called_with(
self.new_backup.id,
)
expected_columns = ('Backup Service', 'Metadata')
self.assertEqual(columns, expected_columns)
def test_backup_export_json(self):
arglist = [
self.new_backup.name,
]
verifylist = [
("backup", self.new_backup.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
parsed_args.formatter = 'json'
columns, __ = self.cmd.take_action(parsed_args)
self.backups_mock.export_record.assert_called_with(
self.new_backup.id,
)
expected_columns = ('backup_service', 'backup_url')
self.assertEqual(columns, expected_columns)
class TestBackupRecordImport(TestBackupRecord):
new_backup = volume_fakes.FakeBackup.create_one_backup(
attrs={'volume_id': 'a54708a2-0388-4476-a909-09579f885c25'})
new_import = volume_fakes.FakeBackup.import_backup_record()
def setUp(self):
super(TestBackupRecordImport, self).setUp()
self.backups_mock.import_record.return_value = self.new_import
# Get the command object to mock
self.cmd = backup_record.ImportBackupRecord(self.app, None)
def test_backup_import(self):
arglist = [
"cinder.backup.drivers.swift.SwiftBackupDriver",
"fake_backup_record_data",
]
verifylist = [
("backup_service",
"cinder.backup.drivers.swift.SwiftBackupDriver"),
("backup_metadata", "fake_backup_record_data"),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, __ = self.cmd.take_action(parsed_args)
self.backups_mock.import_record.assert_called_with(
"cinder.backup.drivers.swift.SwiftBackupDriver",
"fake_backup_record_data",
)
self.assertEqual(columns, ('backup',))

View File

@ -0,0 +1,82 @@
#
# 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.
#
"""Volume v2 Backup action implementations"""
import logging
from osc_lib.command import command
from osc_lib import utils
import six
from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
class ExportBackupRecord(command.ShowOne):
_description = _('Export volume backup details. Backup information can be '
'imported into a new service instance to be able to '
'restore.')
def get_parser(self, prog_name):
parser = super(ExportBackupRecord, self).get_parser(prog_name)
parser.add_argument(
"backup",
metavar="<backup>",
help=_("Backup to export (name or ID)")
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
backup = utils.find_resource(volume_client.backups, parsed_args.backup)
backup_data = volume_client.backups.export_record(backup.id)
# We only want to show "friendly" display names, but also want to keep
# json structure compatibility with cinderclient
if parsed_args.formatter == 'table':
backup_data['Backup Service'] = backup_data.pop('backup_service')
backup_data['Metadata'] = backup_data.pop('backup_url')
return zip(*sorted(six.iteritems(backup_data)))
class ImportBackupRecord(command.ShowOne):
_description = _('Import volume backup details. Exported backup details '
'contain the metadata necessary to restore to a new or '
'rebuilt service instance')
def get_parser(self, prog_name):
parser = super(ImportBackupRecord, self).get_parser(prog_name)
parser.add_argument(
"backup_service",
metavar="<backup_service>",
help=_("Backup service containing the backup.")
)
parser.add_argument(
"backup_metadata",
metavar="<backup_metadata>",
help=_("Encoded backup metadata from export.")
)
return parser
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
backup_data = volume_client.backups.import_record(
parsed_args.backup_service,
parsed_args.backup_metadata)
backup_data.pop('links', None)
return zip(*sorted(six.iteritems(backup_data)))

View File

@ -0,0 +1,15 @@
---
features:
- |
Add ``openstack volume backup record export <backup>`` command that
provides encrypted backup record metadata that can then be used to import
and restore on a different or rebuilt block storage service. Information
about volume backup export and import can be found in the `Cinder block
storage service documentation <https://docs.openstack.org/cinder/latest/admin/blockstorage-volume-backups-export-import.html>`_.
- |
Add ``openstack volume backup record import <backup_service>
<backup_matadata>`` command that can be used to import and restore on a
different or rebuilt block storage service. Information about volume backup
export and import can be found in the `Cinder block storage service
documentation <https://docs.openstack.org/cinder/latest/admin/blockstorage-volume-backups-export-import.html>`_.

View File

@ -629,6 +629,9 @@ openstack.volume.v2 =
volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord
volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord
volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability
volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool