Browse Source

Merge "RemoteFS: enable image volume cache"

tags/11.0.0.0b3
Jenkins 1 year ago
parent
commit
673269858c
2 changed files with 117 additions and 63 deletions
  1. 85
    46
      cinder/tests/unit/volume/drivers/test_remotefs.py
  2. 32
    17
      cinder/volume/drivers/remotefs.py

+ 85
- 46
cinder/tests/unit/volume/drivers/test_remotefs.py View File

@@ -52,9 +52,26 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
52 52
                                     self._fake_snapshot.id)
53 53
         self._fake_snapshot.volume = self._fake_volume
54 54
 
55
+    @ddt.data({'current_state': 'in-use',
56
+               'acceptable_states': ['available', 'in-use']},
57
+              {'current_state': 'in-use',
58
+               'acceptable_states': ['available'],
59
+               'expected_exception': exception.InvalidVolume})
60
+    @ddt.unpack
61
+    def test_validate_state(self, current_state, acceptable_states,
62
+                            expected_exception=None):
63
+        if expected_exception:
64
+            self.assertRaises(expected_exception,
65
+                              self._driver._validate_state,
66
+                              current_state,
67
+                              acceptable_states)
68
+        else:
69
+            self._driver._validate_state(current_state, acceptable_states)
70
+
55 71
     def _test_delete_snapshot(self, volume_in_use=False,
56 72
                               stale_snapshot=False,
57
-                              is_active_image=True):
73
+                              is_active_image=True,
74
+                              is_tmp_snap=False):
58 75
         # If the snapshot is not the active image, it is guaranteed that
59 76
         # another snapshot exists having it as backing file.
60 77
 
@@ -78,6 +95,7 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
78 95
         self._driver._local_volume_dir = mock.Mock(
79 96
             return_value=self._FAKE_MNT_POINT)
80 97
 
98
+        self._driver._validate_state = mock.Mock()
81 99
         self._driver._read_info_file = mock.Mock()
82 100
         self._driver._write_info_file = mock.Mock()
83 101
         self._driver._img_commit = mock.Mock()
@@ -91,12 +109,18 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
91 109
             self._fake_snapshot.id: fake_snapshot_name
92 110
         }
93 111
 
112
+        exp_acceptable_states = ['available', 'in-use', 'backing-up',
113
+                                 'deleting', 'downloading']
114
+
94 115
         if volume_in_use:
95 116
             self._fake_snapshot.volume.status = 'in-use'
96 117
 
97 118
             self._driver._read_info_file.return_value = fake_info
98 119
 
99 120
             self._driver._delete_snapshot(self._fake_snapshot)
121
+            self._driver._validate_state.assert_called_once_with(
122
+                self._fake_snapshot.volume.status,
123
+                exp_acceptable_states)
100 124
             if stale_snapshot:
101 125
                 self._driver._delete_stale_snapshot.assert_called_once_with(
102 126
                     self._fake_snapshot)
@@ -228,7 +252,7 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
228 252
                  mock.call(*command3, run_as_root=True)]
229 253
         self._driver._execute.assert_has_calls(calls)
230 254
 
231
-    def _test_create_snapshot(self, volume_in_use=False):
255
+    def _test_create_snapshot(self, volume_in_use=False, tmp_snap=False):
232 256
         fake_snapshot_info = {}
233 257
         fake_snapshot_file_name = os.path.basename(self._fake_snapshot_path)
234 258
 
@@ -243,11 +267,16 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
243 267
             return_value=self._fake_volume.name)
244 268
         self._driver._get_new_snap_path = mock.Mock(
245 269
             return_value=self._fake_snapshot_path)
270
+        self._driver._validate_state = mock.Mock()
246 271
 
247 272
         expected_snapshot_info = {
248 273
             'active': fake_snapshot_file_name,
249 274
             self._fake_snapshot.id: fake_snapshot_file_name
250 275
         }
276
+        exp_acceptable_states = ['available', 'in-use', 'backing-up']
277
+        if tmp_snap:
278
+            exp_acceptable_states.append('downloading')
279
+            self._fake_snapshot.id = 'tmp-snap-%s' % self._fake_snapshot.id
251 280
 
252 281
         if volume_in_use:
253 282
             self._fake_snapshot.volume.status = 'in-use'
@@ -258,6 +287,9 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
258 287
 
259 288
         self._driver._create_snapshot(self._fake_snapshot)
260 289
 
290
+        self._driver._validate_state.assert_called_once_with(
291
+            self._fake_snapshot.volume.status,
292
+            exp_acceptable_states)
261 293
         fake_method = getattr(self._driver, expected_method_called)
262 294
         fake_method.assert_called_with(
263 295
             self._fake_snapshot, self._fake_volume.name,
@@ -428,52 +460,59 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
428 460
                                  basedir=basedir,
429 461
                                  valid_backing_file=False)
430 462
 
431
-    def test_create_cloned_volume(self):
463
+    @mock.patch.object(remotefs.RemoteFSSnapDriver,
464
+                       '_validate_state')
465
+    @mock.patch.object(remotefs.RemoteFSSnapDriver, '_create_snapshot')
466
+    @mock.patch.object(remotefs.RemoteFSSnapDriver, '_delete_snapshot')
467
+    @mock.patch.object(remotefs.RemoteFSSnapDriver,
468
+                       '_copy_volume_from_snapshot')
469
+    def test_create_cloned_volume(self, mock_copy_volume_from_snapshot,
470
+                                  mock_delete_snapshot,
471
+                                  mock_create_snapshot,
472
+                                  mock_validate_state):
432 473
         drv = self._driver
433 474
 
434
-        with mock.patch.object(drv, '_create_snapshot') as \
435
-                mock_create_snapshot,\
436
-                mock.patch.object(drv, '_delete_snapshot') as \
437
-                mock_delete_snapshot,\
438
-                mock.patch.object(drv, '_copy_volume_from_snapshot') as \
439
-                mock_copy_volume_from_snapshot:
440
-
441
-            volume = fake_volume.fake_volume_obj(self.context)
442
-            src_vref_id = '375e32b2-804a-49f2-b282-85d1d5a5b9e1'
443
-            src_vref = fake_volume.fake_volume_obj(
444
-                self.context,
445
-                id=src_vref_id,
446
-                name='volume-%s' % src_vref_id)
447
-
448
-            vol_attrs = ['provider_location', 'size', 'id', 'name', 'status',
449
-                         'volume_type', 'metadata']
450
-            Volume = collections.namedtuple('Volume', vol_attrs)
451
-
452
-            snap_attrs = ['volume_name', 'volume_size', 'name',
453
-                          'volume_id', 'id', 'volume']
454
-            Snapshot = collections.namedtuple('Snapshot', snap_attrs)
455
-
456
-            volume_ref = Volume(id=volume.id,
457
-                                name=volume.name,
458
-                                status=volume.status,
459
-                                provider_location=volume.provider_location,
460
-                                size=volume.size,
461
-                                volume_type=volume.volume_type,
462
-                                metadata=volume.metadata)
463
-
464
-            snap_ref = Snapshot(volume_name=volume.name,
465
-                                name='clone-snap-%s' % src_vref.id,
466
-                                volume_size=src_vref.size,
467
-                                volume_id=src_vref.id,
468
-                                id='tmp-snap-%s' % src_vref.id,
469
-                                volume=src_vref)
470
-
471
-            drv.create_cloned_volume(volume, src_vref)
472
-
473
-            mock_create_snapshot.assert_called_once_with(snap_ref)
474
-            mock_copy_volume_from_snapshot.assert_called_once_with(
475
-                snap_ref, volume_ref, volume['size'])
476
-            self.assertTrue(mock_delete_snapshot.called)
475
+        volume = fake_volume.fake_volume_obj(self.context)
476
+        src_vref_id = '375e32b2-804a-49f2-b282-85d1d5a5b9e1'
477
+        src_vref = fake_volume.fake_volume_obj(
478
+            self.context,
479
+            id=src_vref_id,
480
+            name='volume-%s' % src_vref_id)
481
+
482
+        vol_attrs = ['provider_location', 'size', 'id', 'name', 'status',
483
+                     'volume_type', 'metadata']
484
+        Volume = collections.namedtuple('Volume', vol_attrs)
485
+
486
+        snap_attrs = ['volume_name', 'volume_size', 'name',
487
+                      'volume_id', 'id', 'volume']
488
+        Snapshot = collections.namedtuple('Snapshot', snap_attrs)
489
+
490
+        volume_ref = Volume(id=volume.id,
491
+                            name=volume.name,
492
+                            status=volume.status,
493
+                            provider_location=volume.provider_location,
494
+                            size=volume.size,
495
+                            volume_type=volume.volume_type,
496
+                            metadata=volume.metadata)
497
+
498
+        snap_ref = Snapshot(volume_name=volume.name,
499
+                            name='clone-snap-%s' % src_vref.id,
500
+                            volume_size=src_vref.size,
501
+                            volume_id=src_vref.id,
502
+                            id='tmp-snap-%s' % src_vref.id,
503
+                            volume=src_vref)
504
+
505
+        drv.create_cloned_volume(volume, src_vref)
506
+
507
+        exp_acceptable_states = ['available', 'backing-up', 'downloading']
508
+        mock_validate_state.assert_called_once_with(
509
+            src_vref.status,
510
+            exp_acceptable_states,
511
+            obj_description='source volume')
512
+        mock_create_snapshot.assert_called_once_with(snap_ref)
513
+        mock_copy_volume_from_snapshot.assert_called_once_with(
514
+            snap_ref, volume_ref, volume['size'])
515
+        self.assertTrue(mock_delete_snapshot.called)
477 516
 
478 517
     def test_create_regular_file(self):
479 518
         self._driver._create_regular_file('/path', 1)

+ 32
- 17
cinder/volume/drivers/remotefs.py View File

@@ -233,6 +233,23 @@ class RemoteFSDriver(driver.BaseVD):
233 233
                   " mount_point_base.")
234 234
         return None
235 235
 
236
+    @staticmethod
237
+    def _validate_state(current_state,
238
+                        acceptable_states,
239
+                        obj_description='volume',
240
+                        invalid_exc=exception.InvalidVolume):
241
+        if current_state not in acceptable_states:
242
+            message = _('Invalid %(obj_description)s state. '
243
+                        'Acceptable states for this operation: '
244
+                        '%(acceptable_states)s. '
245
+                        'Current %(obj_description)s state: '
246
+                        '%(current_state)s.')
247
+            raise invalid_exc(
248
+                message=message %
249
+                dict(obj_description=obj_description,
250
+                     acceptable_states=acceptable_states,
251
+                     current_state=current_state))
252
+
236 253
     @utils.trace
237 254
     def create_volume(self, volume):
238 255
         """Creates a volume.
@@ -941,11 +958,10 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
941 958
                  {'src': src_vref.id,
942 959
                   'dst': volume.id})
943 960
 
944
-        if src_vref.status not in ['available', 'backing-up']:
945
-            msg = _("Source volume status must be 'available', or "
946
-                    "'backing-up' but is: "
947
-                    "%(status)s.") % {'status': src_vref.status}
948
-            raise exception.InvalidVolume(msg)
961
+        acceptable_states = ['available', 'backing-up', 'downloading']
962
+        self._validate_state(src_vref.status,
963
+                             acceptable_states,
964
+                             obj_description='source volume')
949 965
 
950 966
         volume_name = CONF.volume_name_template % volume.id
951 967
 
@@ -1021,13 +1037,9 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
1021 1037
                             else 'offline')})
1022 1038
 
1023 1039
         volume_status = snapshot.volume.status
1024
-        if volume_status not in ['available', 'in-use',
1025
-                                 'backing-up', 'deleting']:
1026
-            msg = _("Volume status must be 'available', 'in-use', "
1027
-                    "'backing-up' or 'deleting' but is: "
1028
-                    "%(status)s.") % {'status': volume_status}
1029
-
1030
-            raise exception.InvalidVolume(msg)
1040
+        acceptable_states = ['available', 'in-use', 'backing-up', 'deleting',
1041
+                             'downloading']
1042
+        self._validate_state(volume_status, acceptable_states)
1031 1043
 
1032 1044
         vol_path = self._local_volume_dir(snapshot.volume)
1033 1045
         self._ensure_share_writable(vol_path)
@@ -1332,12 +1344,15 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
1332 1344
                             else 'offline')})
1333 1345
 
1334 1346
         status = snapshot.volume.status
1335
-        if status not in ['available', 'in-use', 'backing-up']:
1336
-            msg = _("Volume status must be 'available', 'in-use' or "
1337
-                    "'backing-up' but is: "
1338
-                    "%(status)s.") % {'status': status}
1339 1347
 
1340
-            raise exception.InvalidVolume(msg)
1348
+        acceptable_states = ['available', 'in-use', 'backing-up']
1349
+        if snapshot.id.startswith('tmp-snap-'):
1350
+            # This is an internal volume snapshot. In order to support
1351
+            # image caching, we'll allow creating/deleting such snapshots
1352
+            # while having volumes in 'downloading' state.
1353
+            acceptable_states.append('downloading')
1354
+
1355
+        self._validate_state(status, acceptable_states)
1341 1356
 
1342 1357
         info_path = self._local_path_volume_info(snapshot.volume)
1343 1358
         snap_info = self._read_info_file(info_path, empty_if_missing=True)

Loading…
Cancel
Save