self.validate_deletion_policy(self.t)
return self.properties.validate()
- @staticmethod
- def validate_deletion_policy(template):
+ @classmethod
+ def validate_deletion_policy(cls, template):
deletion_policy = template.get('DeletionPolicy', 'Delete')
if deletion_policy not in ('Delete', 'Retain', 'Snapshot'):
msg = 'Invalid DeletionPolicy %s' % deletion_policy
raise exception.StackValidationFailed(message=msg)
elif deletion_policy == 'Snapshot':
- # Some resources will support it in the future, in which case we
- # should check for the presence of a handle_snapshot method for
- # example.
- msg = 'Snapshot DeletionPolicy not supported'
- raise exception.StackValidationFailed(message=msg)
+ if not callable(getattr(cls, 'handle_snapshot', None)):
+ msg = 'Snapshot DeletionPolicy not supported'
+ raise exception.StackValidationFailed(message=msg)
def delete(self):
'''
try:
self.state_set(self.DELETE_IN_PROGRESS)
- if self.t.get('DeletionPolicy', 'Delete') == 'Delete':
+ deletion_policy = self.t.get('DeletionPolicy', 'Delete')
+ if deletion_policy == 'Delete':
if callable(getattr(self, 'handle_delete', None)):
self.handle_delete()
+ elif deletion_policy == 'Snapshot':
+ if callable(getattr(self, 'handle_snapshot', None)):
+ self.handle_snapshot()
except Exception as ex:
logger.exception('Delete %s', str(self))
failure = exception.ResourceFailure(ex)
from heat.engine import scheduler
from heat.engine.resources import volume as vol
from heat.engine import clients
+from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
from heat.tests.v1_1 import fakes
-from heat.tests.utils import setup_dummy_db
+from heat.tests.utils import setup_dummy_db, skip_if
+
+from cinderclient.v1 import client as cinderclient
+
+
+volume_backups = try_import('cinderclient.v1.volume_backups')
class VolumeTest(HeatTestCase):
def setUp(self):
super(VolumeTest, self).setUp()
self.fc = fakes.FakeClient()
+ self.cinder_fc = cinderclient.Client('username', 'password')
self.m.StubOutWithMock(clients.OpenStackClients, 'cinder')
self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
- self.m.StubOutWithMock(self.fc.volumes, 'create')
- self.m.StubOutWithMock(self.fc.volumes, 'get')
- self.m.StubOutWithMock(self.fc.volumes, 'delete')
+ self.m.StubOutWithMock(self.cinder_fc.volumes, 'create')
+ self.m.StubOutWithMock(self.cinder_fc.volumes, 'get')
+ self.m.StubOutWithMock(self.cinder_fc.volumes, 'delete')
self.m.StubOutWithMock(self.fc.volumes, 'create_server_volume')
self.m.StubOutWithMock(self.fc.volumes, 'delete_server_volume')
self.m.StubOutWithMock(eventlet, 'sleep')
stack_name = 'test_volume_stack'
# create script
- clients.OpenStackClients.cinder().MultipleTimes().AndReturn(self.fc)
- self.fc.volumes.create(
+ clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+ self.cinder_fc)
+ self.cinder_fc.volumes.create(
u'1', display_description='%s.DataVolume' % stack_name,
display_name='%s.DataVolume' % stack_name).AndReturn(fv)
# delete script
- self.fc.volumes.get('vol-123').AndReturn(fv)
+ self.cinder_fc.volumes.get('vol-123').AndReturn(fv)
eventlet.sleep(1).AndReturn(None)
- self.fc.volumes.get('vol-123').AndReturn(fv)
- self.fc.volumes.delete('vol-123').AndReturn(None)
+ self.cinder_fc.volumes.get('vol-123').AndReturn(fv)
+ self.cinder_fc.volumes.delete('vol-123').AndReturn(None)
- self.fc.volumes.get('vol-123').AndRaise(
+ self.cinder_fc.volumes.get('vol-123').AndRaise(
clients.cinder_exceptions.NotFound('Not found'))
self.m.ReplayAll()
stack_name = 'test_volume_create_error_stack'
# create script
- clients.OpenStackClients.cinder().AndReturn(self.fc)
- self.fc.volumes.create(
+ clients.OpenStackClients.cinder().AndReturn(self.cinder_fc)
+ self.cinder_fc.volumes.create(
u'1', display_description='%s.DataVolume' % stack_name,
display_name='%s.DataVolume' % stack_name).AndReturn(fv)
stack_name = 'test_volume_attach_error_stack'
# volume create
- clients.OpenStackClients.cinder().MultipleTimes().AndReturn(self.fc)
- self.fc.volumes.create(
+ clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+ self.cinder_fc)
+ self.cinder_fc.volumes.create(
u'1', display_description='%s.DataVolume' % stack_name,
display_name='%s.DataVolume' % stack_name).AndReturn(fv)
# create script
clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc)
-# clients.OpenStackClients.cinder().MultipleTimes().AndReturn(self.fc)
eventlet.sleep(1).MultipleTimes().AndReturn(None)
self.fc.volumes.create_server_volume(
server_id=u'WikiDatabase',
volume_id=u'vol-123').AndReturn(fva)
- self.fc.volumes.get('vol-123').AndReturn(fva)
+ self.cinder_fc.volumes.get('vol-123').AndReturn(fva)
self.m.ReplayAll()
stack_name = 'test_volume_attach_stack'
# volume create
- clients.OpenStackClients.cinder().MultipleTimes().AndReturn(self.fc)
- self.fc.volumes.create(
+ clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+ self.cinder_fc)
+ self.cinder_fc.volumes.create(
u'1', display_description='%s.DataVolume' % stack_name,
display_name='%s.DataVolume' % stack_name).AndReturn(fv)
server_id=u'WikiDatabase',
volume_id=u'vol-123').AndReturn(fva)
- self.fc.volumes.get('vol-123').AndReturn(fva)
+ self.cinder_fc.volumes.get('vol-123').AndReturn(fva)
# delete script
fva = FakeVolume('in-use', 'available')
self.fc.volumes.delete_server_volume('WikiDatabase',
'vol-123').AndReturn(None)
- self.fc.volumes.get('vol-123').AndReturn(fva)
+ self.cinder_fc.volumes.get('vol-123').AndReturn(fva)
self.m.ReplayAll()
self.m.VerifyAll()
+ @skip_if(volume_backups is None, 'unable to import volume_backups')
+ def test_snapshot(self):
+ stack_name = 'test_volume_stack'
+ fv = FakeVolume('creating', 'available')
+ fb = FakeBackup('creating', 'available')
+
+ # create script
+ clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+ self.cinder_fc)
+ self.cinder_fc.volumes.create(
+ u'1', display_description='%s.DataVolume' % stack_name,
+ display_name='%s.DataVolume' % stack_name).AndReturn(fv)
+ eventlet.sleep(1).AndReturn(None)
+
+ # snapshot script
+ self.m.StubOutWithMock(self.cinder_fc.backups, 'create')
+ self.cinder_fc.backups.create('vol-123').AndReturn(fb)
+ eventlet.sleep(1).AndReturn(None)
+ self.cinder_fc.volumes.get('vol-123').AndReturn(fv)
+ self.cinder_fc.volumes.delete('vol-123').AndReturn(None)
+ self.m.ReplayAll()
+
+ t = self.load_template()
+ t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot'
+ stack = self.parse_stack(t, stack_name)
+
+ resource = self.create_volume(t, stack, 'DataVolume')
+
+ self.assertEqual(resource.destroy(), None)
+
+ self.m.VerifyAll()
+
+ @skip_if(volume_backups is None, 'unable to import volume_backups')
+ def test_snapshot_error(self):
+ stack_name = 'test_volume_stack'
+ fv = FakeVolume('creating', 'available')
+ fb = FakeBackup('creating', 'error')
+
+ # create script
+ clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+ self.cinder_fc)
+ self.cinder_fc.volumes.create(
+ u'1', display_description='%s.DataVolume' % stack_name,
+ display_name='%s.DataVolume' % stack_name).AndReturn(fv)
+ eventlet.sleep(1).AndReturn(None)
+
+ # snapshot script
+ self.m.StubOutWithMock(self.cinder_fc.backups, 'create')
+ self.cinder_fc.backups.create('vol-123').AndReturn(fb)
+ eventlet.sleep(1).AndReturn(None)
+ self.m.ReplayAll()
+
+ t = self.load_template()
+ t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot'
+ stack = self.parse_stack(t, stack_name)
+
+ resource = self.create_volume(t, stack, 'DataVolume')
+
+ self.assertRaises(exception.ResourceFailure, resource.destroy)
+
+ self.m.VerifyAll()
+
+ def test_snapshot_no_volume(self):
+ stack_name = 'test_volume_stack'
+ fv = FakeVolume('creating', 'error')
+
+ # create script
+ clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+ self.cinder_fc)
+ self.cinder_fc.volumes.create(
+ u'1', display_description='%s.DataVolume' % stack_name,
+ display_name='%s.DataVolume' % stack_name).AndReturn(fv)
+ eventlet.sleep(1).AndReturn(None)
+
+ self.m.ReplayAll()
+
+ t = self.load_template()
+ t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot'
+ stack = self.parse_stack(t, stack_name)
+ resource = vol.Volume('DataVolume',
+ t['Resources']['DataVolume'],
+ stack)
+
+ create = scheduler.TaskRunner(resource.create)
+ self.assertRaises(exception.ResourceFailure, create)
+
+ self.assertEqual(resource.destroy(), None)
+
+ self.m.VerifyAll()
+
class FakeVolume:
status = 'attaching'
def get(self):
self.status = self.final_status
+
+
+class FakeBackup(FakeVolume):
+ status = 'creating'
+ id = 'backup-123'