summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-06-15 00:22:52 +0000
committerGerrit Code Review <review@openstack.org>2017-06-15 00:22:52 +0000
commit673269858ceacf27808a654d66ca4b221bbe21b4 (patch)
treea91ad2bb181652eadd80edc46b2418af3e9fe168
parent82efcbf2a7d189771f331e98b18d73737e082735 (diff)
parent63b9f0fb8e831c29fab00670c19056e436d5f5f0 (diff)
Merge "RemoteFS: enable image volume cache"
-rw-r--r--cinder/tests/unit/volume/drivers/test_remotefs.py131
-rw-r--r--cinder/volume/drivers/remotefs.py49
2 files changed, 117 insertions, 63 deletions
diff --git a/cinder/tests/unit/volume/drivers/test_remotefs.py b/cinder/tests/unit/volume/drivers/test_remotefs.py
index 2e1caec..24e7d43 100644
--- a/cinder/tests/unit/volume/drivers/test_remotefs.py
+++ b/cinder/tests/unit/volume/drivers/test_remotefs.py
@@ -52,9 +52,26 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
52 self._fake_snapshot.id) 52 self._fake_snapshot.id)
53 self._fake_snapshot.volume = self._fake_volume 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 def _test_delete_snapshot(self, volume_in_use=False, 71 def _test_delete_snapshot(self, volume_in_use=False,
56 stale_snapshot=False, 72 stale_snapshot=False,
57 is_active_image=True): 73 is_active_image=True,
74 is_tmp_snap=False):
58 # If the snapshot is not the active image, it is guaranteed that 75 # If the snapshot is not the active image, it is guaranteed that
59 # another snapshot exists having it as backing file. 76 # another snapshot exists having it as backing file.
60 77
@@ -78,6 +95,7 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
78 self._driver._local_volume_dir = mock.Mock( 95 self._driver._local_volume_dir = mock.Mock(
79 return_value=self._FAKE_MNT_POINT) 96 return_value=self._FAKE_MNT_POINT)
80 97
98 self._driver._validate_state = mock.Mock()
81 self._driver._read_info_file = mock.Mock() 99 self._driver._read_info_file = mock.Mock()
82 self._driver._write_info_file = mock.Mock() 100 self._driver._write_info_file = mock.Mock()
83 self._driver._img_commit = mock.Mock() 101 self._driver._img_commit = mock.Mock()
@@ -91,12 +109,18 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
91 self._fake_snapshot.id: fake_snapshot_name 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 if volume_in_use: 115 if volume_in_use:
95 self._fake_snapshot.volume.status = 'in-use' 116 self._fake_snapshot.volume.status = 'in-use'
96 117
97 self._driver._read_info_file.return_value = fake_info 118 self._driver._read_info_file.return_value = fake_info
98 119
99 self._driver._delete_snapshot(self._fake_snapshot) 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 if stale_snapshot: 124 if stale_snapshot:
101 self._driver._delete_stale_snapshot.assert_called_once_with( 125 self._driver._delete_stale_snapshot.assert_called_once_with(
102 self._fake_snapshot) 126 self._fake_snapshot)
@@ -228,7 +252,7 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
228 mock.call(*command3, run_as_root=True)] 252 mock.call(*command3, run_as_root=True)]
229 self._driver._execute.assert_has_calls(calls) 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 fake_snapshot_info = {} 256 fake_snapshot_info = {}
233 fake_snapshot_file_name = os.path.basename(self._fake_snapshot_path) 257 fake_snapshot_file_name = os.path.basename(self._fake_snapshot_path)
234 258
@@ -243,11 +267,16 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
243 return_value=self._fake_volume.name) 267 return_value=self._fake_volume.name)
244 self._driver._get_new_snap_path = mock.Mock( 268 self._driver._get_new_snap_path = mock.Mock(
245 return_value=self._fake_snapshot_path) 269 return_value=self._fake_snapshot_path)
270 self._driver._validate_state = mock.Mock()
246 271
247 expected_snapshot_info = { 272 expected_snapshot_info = {
248 'active': fake_snapshot_file_name, 273 'active': fake_snapshot_file_name,
249 self._fake_snapshot.id: fake_snapshot_file_name 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 if volume_in_use: 281 if volume_in_use:
253 self._fake_snapshot.volume.status = 'in-use' 282 self._fake_snapshot.volume.status = 'in-use'
@@ -258,6 +287,9 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
258 287
259 self._driver._create_snapshot(self._fake_snapshot) 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 fake_method = getattr(self._driver, expected_method_called) 293 fake_method = getattr(self._driver, expected_method_called)
262 fake_method.assert_called_with( 294 fake_method.assert_called_with(
263 self._fake_snapshot, self._fake_volume.name, 295 self._fake_snapshot, self._fake_volume.name,
@@ -428,52 +460,59 @@ class RemoteFsSnapDriverTestCase(test.TestCase):
428 basedir=basedir, 460 basedir=basedir,
429 valid_backing_file=False) 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 drv = self._driver 473 drv = self._driver
433 474
434 with mock.patch.object(drv, '_create_snapshot') as \ 475 volume = fake_volume.fake_volume_obj(self.context)
435 mock_create_snapshot,\ 476 src_vref_id = '375e32b2-804a-49f2-b282-85d1d5a5b9e1'
436 mock.patch.object(drv, '_delete_snapshot') as \ 477 src_vref = fake_volume.fake_volume_obj(
437 mock_delete_snapshot,\ 478 self.context,
438 mock.patch.object(drv, '_copy_volume_from_snapshot') as \ 479 id=src_vref_id,
439 mock_copy_volume_from_snapshot: 480 name='volume-%s' % src_vref_id)
440 481
441 volume = fake_volume.fake_volume_obj(self.context) 482 vol_attrs = ['provider_location', 'size', 'id', 'name', 'status',
442 src_vref_id = '375e32b2-804a-49f2-b282-85d1d5a5b9e1' 483 'volume_type', 'metadata']
443 src_vref = fake_volume.fake_volume_obj( 484 Volume = collections.namedtuple('Volume', vol_attrs)
444 self.context, 485
445 id=src_vref_id, 486 snap_attrs = ['volume_name', 'volume_size', 'name',
446 name='volume-%s' % src_vref_id) 487 'volume_id', 'id', 'volume']
447 488 Snapshot = collections.namedtuple('Snapshot', snap_attrs)
448 vol_attrs = ['provider_location', 'size', 'id', 'name', 'status', 489
449 'volume_type', 'metadata'] 490 volume_ref = Volume(id=volume.id,
450 Volume = collections.namedtuple('Volume', vol_attrs) 491 name=volume.name,
451 492 status=volume.status,
452 snap_attrs = ['volume_name', 'volume_size', 'name', 493 provider_location=volume.provider_location,
453 'volume_id', 'id', 'volume'] 494 size=volume.size,
454 Snapshot = collections.namedtuple('Snapshot', snap_attrs) 495 volume_type=volume.volume_type,
455 496 metadata=volume.metadata)
456 volume_ref = Volume(id=volume.id, 497
457 name=volume.name, 498 snap_ref = Snapshot(volume_name=volume.name,
458 status=volume.status, 499 name='clone-snap-%s' % src_vref.id,
459 provider_location=volume.provider_location, 500 volume_size=src_vref.size,
460 size=volume.size, 501 volume_id=src_vref.id,
461 volume_type=volume.volume_type, 502 id='tmp-snap-%s' % src_vref.id,
462 metadata=volume.metadata) 503 volume=src_vref)
463 504
464 snap_ref = Snapshot(volume_name=volume.name, 505 drv.create_cloned_volume(volume, src_vref)
465 name='clone-snap-%s' % src_vref.id, 506
466 volume_size=src_vref.size, 507 exp_acceptable_states = ['available', 'backing-up', 'downloading']
467 volume_id=src_vref.id, 508 mock_validate_state.assert_called_once_with(
468 id='tmp-snap-%s' % src_vref.id, 509 src_vref.status,
469 volume=src_vref) 510 exp_acceptable_states,
470 511 obj_description='source volume')
471 drv.create_cloned_volume(volume, src_vref) 512 mock_create_snapshot.assert_called_once_with(snap_ref)
472 513 mock_copy_volume_from_snapshot.assert_called_once_with(
473 mock_create_snapshot.assert_called_once_with(snap_ref) 514 snap_ref, volume_ref, volume['size'])
474 mock_copy_volume_from_snapshot.assert_called_once_with( 515 self.assertTrue(mock_delete_snapshot.called)
475 snap_ref, volume_ref, volume['size'])
476 self.assertTrue(mock_delete_snapshot.called)
477 516
478 def test_create_regular_file(self): 517 def test_create_regular_file(self):
479 self._driver._create_regular_file('/path', 1) 518 self._driver._create_regular_file('/path', 1)
diff --git a/cinder/volume/drivers/remotefs.py b/cinder/volume/drivers/remotefs.py
index 99481bd..4aa2939 100644
--- a/cinder/volume/drivers/remotefs.py
+++ b/cinder/volume/drivers/remotefs.py
@@ -233,6 +233,23 @@ class RemoteFSDriver(driver.BaseVD):
233 " mount_point_base.") 233 " mount_point_base.")
234 return None 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 @utils.trace 253 @utils.trace
237 def create_volume(self, volume): 254 def create_volume(self, volume):
238 """Creates a volume. 255 """Creates a volume.
@@ -941,11 +958,10 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
941 {'src': src_vref.id, 958 {'src': src_vref.id,
942 'dst': volume.id}) 959 'dst': volume.id})
943 960
944 if src_vref.status not in ['available', 'backing-up']: 961 acceptable_states = ['available', 'backing-up', 'downloading']
945 msg = _("Source volume status must be 'available', or " 962 self._validate_state(src_vref.status,
946 "'backing-up' but is: " 963 acceptable_states,
947 "%(status)s.") % {'status': src_vref.status} 964 obj_description='source volume')
948 raise exception.InvalidVolume(msg)
949 965
950 volume_name = CONF.volume_name_template % volume.id 966 volume_name = CONF.volume_name_template % volume.id
951 967
@@ -1021,13 +1037,9 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
1021 else 'offline')}) 1037 else 'offline')})
1022 1038
1023 volume_status = snapshot.volume.status 1039 volume_status = snapshot.volume.status
1024 if volume_status not in ['available', 'in-use', 1040 acceptable_states = ['available', 'in-use', 'backing-up', 'deleting',
1025 'backing-up', 'deleting']: 1041 'downloading']
1026 msg = _("Volume status must be 'available', 'in-use', " 1042 self._validate_state(volume_status, acceptable_states)
1027 "'backing-up' or 'deleting' but is: "
1028 "%(status)s.") % {'status': volume_status}
1029
1030 raise exception.InvalidVolume(msg)
1031 1043
1032 vol_path = self._local_volume_dir(snapshot.volume) 1044 vol_path = self._local_volume_dir(snapshot.volume)
1033 self._ensure_share_writable(vol_path) 1045 self._ensure_share_writable(vol_path)
@@ -1332,12 +1344,15 @@ class RemoteFSSnapDriverBase(RemoteFSDriver):
1332 else 'offline')}) 1344 else 'offline')})
1333 1345
1334 status = snapshot.volume.status 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 info_path = self._local_path_volume_info(snapshot.volume) 1357 info_path = self._local_path_volume_info(snapshot.volume)
1343 snap_info = self._read_info_file(info_path, empty_if_missing=True) 1358 snap_info = self._read_info_file(info_path, empty_if_missing=True)