Browse Source

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>
tags/3.18.0
Sean McGinnis 7 months ago
parent
commit
b90c780d2b
No account linked to committer's email address

+ 3
- 195
doc/source/cli/command-objects/volume-backup.rst View File

@@ -2,200 +2,8 @@
2 2
 volume backup
3 3
 =============
4 4
 
5
-Block Storage v1, v2
5
+Volume v1, v2
6 6
 
7
-volume backup create
8
---------------------
7
+.. autoprogram-cliff:: openstack.volume.v2
8
+   :command: volume backup *
9 9
 
10
-Create new volume backup
11
-
12
-.. program:: volume backup create
13
-.. code:: bash
14
-
15
-    openstack volume backup create
16
-        [--container <container>]
17
-        [--name <name>]
18
-        [--description <description>]
19
-        [--snapshot <snapshot>]
20
-        [--force]
21
-        [--incremental]
22
-        <volume>
23
-
24
-.. option:: --container <container>
25
-
26
-    Optional backup container name
27
-
28
-.. option:: --name <name>
29
-
30
-    Name of the backup
31
-
32
-.. option:: --description <description>
33
-
34
-    Description of the backup
35
-
36
-.. option:: --snapshot <snapshot>
37
-
38
-    Snapshot to backup (name or ID)
39
-
40
-    *Volume version 2 only*
41
-
42
-.. option:: --force
43
-
44
-    Allow to back up an in-use volume
45
-
46
-    *Volume version 2 only*
47
-
48
-.. option:: --incremental
49
-
50
-    Perform an incremental backup
51
-
52
-    *Volume version 2 only*
53
-
54
-.. _volume_backup_create-backup:
55
-.. describe:: <volume>
56
-
57
-    Volume to backup (name or ID)
58
-
59
-volume backup delete
60
---------------------
61
-
62
-Delete volume backup(s)
63
-
64
-.. program:: volume backup delete
65
-.. code:: bash
66
-
67
-    openstack volume backup delete
68
-        [--force]
69
-        <backup> [<backup> ...]
70
-
71
-.. option:: --force
72
-
73
-    Allow delete in state other than error or available
74
-
75
-    *Volume version 2 only*
76
-
77
-.. _volume_backup_delete-backup:
78
-.. describe:: <backup>
79
-
80
-    Backup(s) to delete (name or ID)
81
-
82
-volume backup list
83
-------------------
84
-
85
-List volume backups
86
-
87
-.. program:: volume backup list
88
-.. code:: bash
89
-
90
-    openstack volume backup list
91
-        [--long]
92
-        [--name <name>]
93
-        [--status <status>]
94
-        [--volume <volume>]
95
-        [--marker <volume-backup>]
96
-        [--limit <num-backups>]
97
-        [--all-projects]
98
-
99
-.. _volume_backup_list-backup:
100
-.. option:: --long
101
-
102
-    List additional fields in output
103
-
104
-.. option:: --name <name>
105
-
106
-    Filters results by the backup name
107
-
108
-.. option:: --status <status>
109
-
110
-    Filters results by the backup status
111
-    ('creating', 'available', 'deleting', 'error', 'restoring' or 'error_restoring')
112
-
113
-.. option:: --volume <volume>
114
-
115
-    Filters results by the volume which they backup (name or ID)"
116
-
117
-.. option:: --marker <volume-backup>
118
-
119
-    The last backup of the previous page (name or ID)
120
-
121
-    *Volume version 2 only*
122
-
123
-.. option:: --limit <num-backups>
124
-
125
-    Maximum number of backups to display
126
-
127
-    *Volume version 2 only*
128
-
129
-.. option:: --all-projects
130
-
131
-    Include all projects (admin only)
132
-
133
-volume backup restore
134
----------------------
135
-
136
-Restore volume backup
137
-
138
-.. program:: volume backup restore
139
-.. code:: bash
140
-
141
-    openstack volume backup restore
142
-        <backup>
143
-        <volume>
144
-
145
-.. _volume_backup_restore-backup:
146
-.. describe:: <backup>
147
-
148
-    Backup to restore (name or ID)
149
-
150
-.. describe:: <volume>
151
-
152
-    Volume to restore to (name or ID)
153
-
154
-volume backup set
155
------------------
156
-
157
-Set volume backup properties
158
-
159
-.. program:: volume backup set
160
-.. code:: bash
161
-
162
-    openstack volume backup set
163
-        [--name <name>]
164
-        [--description <description>]
165
-        [--state <state>]
166
-        <backup>
167
-
168
-.. option:: --name <name>
169
-
170
-    New backup name
171
-
172
-.. option:: --description <description>
173
-
174
-    New backup description
175
-
176
-.. option:: --state <state>
177
-
178
-    New backup state ("available" or "error") (admin only)
179
-    (This option simply changes the state of the backup in the database with
180
-    no regard to actual status, exercise caution when using)
181
-
182
-.. _backup_set-volume-backup:
183
-.. describe:: <backup>
184
-
185
-    Backup to modify (name or ID)
186
-
187
-volume backup show
188
-------------------
189
-
190
-Display volume backup details
191
-
192
-.. program:: volume backup show
193
-.. code:: bash
194
-
195
-    openstack volume backup show
196
-        <backup>
197
-
198
-.. _volume_backup_show-backup:
199
-.. describe:: <backup>
200
-
201
-    Backup to display (name or ID)

+ 2
- 0
doc/source/cli/commands.rst View File

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

+ 2
- 2
doc/source/cli/data/cinder.csv View File

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

+ 29
- 0
openstackclient/tests/unit/volume/v2/fakes.py View File

@@ -596,6 +596,35 @@ class FakeBackup(object):
596 596
 
597 597
         return mock.Mock(side_effect=backups)
598 598
 
599
+    @staticmethod
600
+    def create_backup_record():
601
+        """Gets a fake backup record for a given backup.
602
+
603
+        :return: An "exported" backup record.
604
+        """
605
+
606
+        return {
607
+            'backup_service': 'cinder.backup.drivers.swift.SwiftBackupDriver',
608
+            'backup_url': 'eyJzdGF0dXMiOiAiYXZh',
609
+        }
610
+
611
+    @staticmethod
612
+    def import_backup_record():
613
+        """Creates a fake backup record import response from a backup.
614
+
615
+        :return: The fake backup object that was encoded.
616
+        """
617
+        return {
618
+            'backup': {
619
+                'id': 'backup.id',
620
+                'name': 'backup.name',
621
+                'links': [
622
+                    {'href': 'link1', 'rel': 'self'},
623
+                    {'href': 'link2', 'rel': 'bookmark'},
624
+                ],
625
+            },
626
+        }
627
+
599 628
 
600 629
 class FakeConsistencyGroup(object):
601 630
     """Fake one or more consistency group."""

+ 114
- 0
openstackclient/tests/unit/volume/v2/test_backup_record.py View File

@@ -0,0 +1,114 @@
1
+#
2
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+#   not use this file except in compliance with the License. You may obtain
4
+#   a copy of the License at
5
+#
6
+#        http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+#   Unless required by applicable law or agreed to in writing, software
9
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+#   License for the specific language governing permissions and limitations
12
+#   under the License.
13
+#
14
+
15
+from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
16
+from openstackclient.volume.v2 import backup_record
17
+
18
+
19
+class TestBackupRecord(volume_fakes.TestVolume):
20
+
21
+    def setUp(self):
22
+        super(TestBackupRecord, self).setUp()
23
+
24
+        self.backups_mock = self.app.client_manager.volume.backups
25
+        self.backups_mock.reset_mock()
26
+
27
+
28
+class TestBackupRecordExport(TestBackupRecord):
29
+
30
+    new_backup = volume_fakes.FakeBackup.create_one_backup(
31
+        attrs={'volume_id': 'a54708a2-0388-4476-a909-09579f885c25'})
32
+    new_record = volume_fakes.FakeBackup.create_backup_record()
33
+
34
+    def setUp(self):
35
+        super(TestBackupRecordExport, self).setUp()
36
+
37
+        self.backups_mock.export_record.return_value = self.new_record
38
+        self.backups_mock.get.return_value = self.new_backup
39
+
40
+        # Get the command object to mock
41
+        self.cmd = backup_record.ExportBackupRecord(self.app, None)
42
+
43
+    def test_backup_export_table(self):
44
+        arglist = [
45
+            self.new_backup.name,
46
+        ]
47
+        verifylist = [
48
+            ("backup", self.new_backup.name),
49
+        ]
50
+
51
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
52
+        parsed_args.formatter = 'table'
53
+        columns, __ = self.cmd.take_action(parsed_args)
54
+
55
+        self.backups_mock.export_record.assert_called_with(
56
+            self.new_backup.id,
57
+        )
58
+
59
+        expected_columns = ('Backup Service', 'Metadata')
60
+        self.assertEqual(columns, expected_columns)
61
+
62
+    def test_backup_export_json(self):
63
+        arglist = [
64
+            self.new_backup.name,
65
+        ]
66
+        verifylist = [
67
+            ("backup", self.new_backup.name),
68
+        ]
69
+
70
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
71
+        parsed_args.formatter = 'json'
72
+        columns, __ = self.cmd.take_action(parsed_args)
73
+
74
+        self.backups_mock.export_record.assert_called_with(
75
+            self.new_backup.id,
76
+        )
77
+
78
+        expected_columns = ('backup_service', 'backup_url')
79
+        self.assertEqual(columns, expected_columns)
80
+
81
+
82
+class TestBackupRecordImport(TestBackupRecord):
83
+
84
+    new_backup = volume_fakes.FakeBackup.create_one_backup(
85
+        attrs={'volume_id': 'a54708a2-0388-4476-a909-09579f885c25'})
86
+    new_import = volume_fakes.FakeBackup.import_backup_record()
87
+
88
+    def setUp(self):
89
+        super(TestBackupRecordImport, self).setUp()
90
+
91
+        self.backups_mock.import_record.return_value = self.new_import
92
+
93
+        # Get the command object to mock
94
+        self.cmd = backup_record.ImportBackupRecord(self.app, None)
95
+
96
+    def test_backup_import(self):
97
+        arglist = [
98
+            "cinder.backup.drivers.swift.SwiftBackupDriver",
99
+            "fake_backup_record_data",
100
+        ]
101
+        verifylist = [
102
+            ("backup_service",
103
+             "cinder.backup.drivers.swift.SwiftBackupDriver"),
104
+            ("backup_metadata", "fake_backup_record_data"),
105
+        ]
106
+
107
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
108
+        columns, __ = self.cmd.take_action(parsed_args)
109
+
110
+        self.backups_mock.import_record.assert_called_with(
111
+            "cinder.backup.drivers.swift.SwiftBackupDriver",
112
+            "fake_backup_record_data",
113
+        )
114
+        self.assertEqual(columns, ('backup',))

+ 82
- 0
openstackclient/volume/v2/backup_record.py View File

@@ -0,0 +1,82 @@
1
+#
2
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+#   not use this file except in compliance with the License. You may obtain
4
+#   a copy of the License at
5
+#
6
+#        http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+#   Unless required by applicable law or agreed to in writing, software
9
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+#   License for the specific language governing permissions and limitations
12
+#   under the License.
13
+#
14
+
15
+"""Volume v2 Backup action implementations"""
16
+
17
+import logging
18
+
19
+from osc_lib.command import command
20
+from osc_lib import utils
21
+import six
22
+
23
+from openstackclient.i18n import _
24
+
25
+
26
+LOG = logging.getLogger(__name__)
27
+
28
+
29
+class ExportBackupRecord(command.ShowOne):
30
+    _description = _('Export volume backup details. Backup information can be '
31
+                     'imported into a new service instance to be able to '
32
+                     'restore.')
33
+
34
+    def get_parser(self, prog_name):
35
+        parser = super(ExportBackupRecord, self).get_parser(prog_name)
36
+        parser.add_argument(
37
+            "backup",
38
+            metavar="<backup>",
39
+            help=_("Backup to export (name or ID)")
40
+        )
41
+        return parser
42
+
43
+    def take_action(self, parsed_args):
44
+        volume_client = self.app.client_manager.volume
45
+        backup = utils.find_resource(volume_client.backups, parsed_args.backup)
46
+        backup_data = volume_client.backups.export_record(backup.id)
47
+
48
+        # We only want to show "friendly" display names, but also want to keep
49
+        # json structure compatibility with cinderclient
50
+        if parsed_args.formatter == 'table':
51
+            backup_data['Backup Service'] = backup_data.pop('backup_service')
52
+            backup_data['Metadata'] = backup_data.pop('backup_url')
53
+
54
+        return zip(*sorted(six.iteritems(backup_data)))
55
+
56
+
57
+class ImportBackupRecord(command.ShowOne):
58
+    _description = _('Import volume backup details. Exported backup details '
59
+                     'contain the metadata necessary to restore to a new or '
60
+                     'rebuilt service instance')
61
+
62
+    def get_parser(self, prog_name):
63
+        parser = super(ImportBackupRecord, self).get_parser(prog_name)
64
+        parser.add_argument(
65
+            "backup_service",
66
+            metavar="<backup_service>",
67
+            help=_("Backup service containing the backup.")
68
+        )
69
+        parser.add_argument(
70
+            "backup_metadata",
71
+            metavar="<backup_metadata>",
72
+            help=_("Encoded backup metadata from export.")
73
+        )
74
+        return parser
75
+
76
+    def take_action(self, parsed_args):
77
+        volume_client = self.app.client_manager.volume
78
+        backup_data = volume_client.backups.import_record(
79
+            parsed_args.backup_service,
80
+            parsed_args.backup_metadata)
81
+        backup_data.pop('links', None)
82
+        return zip(*sorted(six.iteritems(backup_data)))

+ 15
- 0
releasenotes/notes/volume-backup-record-9f5987c45e294dc6.yaml View File

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

+ 3
- 0
setup.cfg View File

@@ -629,6 +629,9 @@ openstack.volume.v2 =
629 629
     volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
630 630
     volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
631 631
 
632
+    volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord
633
+    volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord
634
+
632 635
     volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability
633 636
     volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool
634 637
 

Loading…
Cancel
Save