diff --git a/heat/common/exception.py b/heat/common/exception.py index 90736575a..11bb73a62 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -240,6 +240,10 @@ class VolumeNotFound(HeatException): msg_fmt = _("The Volume (%(volume)s) could not be found.") +class VolumeSnapshotNotFound(HeatException): + msg_fmt = _("The VolumeSnapshot (%(snapshot)s) could not be found.") + + class PhysicalResourceNameAmbiguity(HeatException): msg_fmt = _( "Multiple physical resources were found with name (%(name)s).") diff --git a/heat/engine/clients/os/cinder.py b/heat/engine/clients/os/cinder.py index c4b7ee081..aba151f58 100644 --- a/heat/engine/clients/os/cinder.py +++ b/heat/engine/clients/os/cinder.py @@ -94,6 +94,14 @@ class CinderClientPlugin(client_plugin.ClientPlugin): {'volume': volume, 'ex': ex}) raise exception.VolumeNotFound(volume=volume) + def get_volume_snapshot(self, snapshot): + try: + return self.client().volume_snapshots.get(snapshot) + except exceptions.NotFound as ex: + LOG.info(_LI('VolumeSnapshot (%(snapshot)s) not found: %(ex)s'), + {'snapshot': snapshot, 'ex': ex}) + raise exception.VolumeSnapshotNotFound(snapshot=snapshot) + def is_not_found(self, ex): return isinstance(ex, exceptions.NotFound) @@ -111,3 +119,11 @@ class VolumeConstraint(constraints.BaseCustomConstraint): def validate_with_client(self, client, volume): client.client_plugin('cinder').get_volume(volume) + + +class VolumeSnapshotConstraint(constraints.BaseCustomConstraint): + + expected_exceptions = (exception.VolumeSnapshotNotFound,) + + def validate_with_client(self, client, snapshot): + client.client_plugin('cinder').get_volume_snapshot(snapshot) diff --git a/heat/tests/test_cinder_client.py b/heat/tests/test_cinder_client.py index e09589007..e9464ccb4 100644 --- a/heat/tests/test_cinder_client.py +++ b/heat/tests/test_cinder_client.py @@ -47,6 +47,20 @@ class CinderClientPluginTests(common.HeatTestCase): self.m.VerifyAll() + def test_get_snapshot(self): + """Tests the get_volume_snapshot function.""" + snapshot_id = str(uuid.uuid4()) + my_snapshot = self.m.CreateMockAnything() + self.cinder_client.volume_snapshots = self.m.CreateMockAnything() + self.cinder_client.volume_snapshots.get(snapshot_id).MultipleTimes().\ + AndReturn(my_snapshot) + self.m.ReplayAll() + + self.assertEqual(my_snapshot, + self.cinder_plugin.get_volume_snapshot(snapshot_id)) + + self.m.VerifyAll() + class VolumeConstraintTest(common.HeatTestCase): @@ -66,3 +80,23 @@ class VolumeConstraintTest(common.HeatTestCase): self.mock_get_volume.side_effect = exception.VolumeNotFound( volume='bar') self.assertFalse(self.constraint.validate("bar", self.ctx)) + + +class VolumeSnapshotConstraintTest(common.HeatTestCase): + + def setUp(self): + super(VolumeSnapshotConstraintTest, self).setUp() + self.ctx = utils.dummy_context() + self.mock_get_snapshot = mock.Mock() + self.ctx.clients.client_plugin( + 'cinder').get_volume_snapshot = self.mock_get_snapshot + self.constraint = cinder.VolumeSnapshotConstraint() + + def test_validation(self): + self.mock_get_snapshot.return_value = 'snapshot' + self.assertTrue(self.constraint.validate("foo", self.ctx)) + + def test_validation_error(self): + self.mock_get_snapshot.side_effect = exception.VolumeSnapshotNotFound( + snapshot='bar') + self.assertFalse(self.constraint.validate("bar", self.ctx)) diff --git a/setup.cfg b/setup.cfg index 2326be76a..e08c546c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,6 +59,7 @@ heat.constraints = nova.server = heat.engine.clients.os.nova:ServerConstraint nova.keypair = heat.engine.clients.os.nova:KeypairConstraint cinder.volume = heat.engine.clients.os.cinder:VolumeConstraint + cinder.snapshot = heat.engine.clients.os.cinder:VolumeSnapshotConstraint heat.stack_lifecycle_plugins =