]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Implement OS::Cinder::Volume
authorThomas Herve <therve@gmail.com>
Wed, 8 May 2013 12:16:37 +0000 (14:16 +0200)
committerThomas Herve <therve@gmail.com>
Tue, 14 May 2013 12:15:07 +0000 (14:15 +0200)
It creates a new resource type allowing to pass cinder specific
arguments in the template.

Change-Id: I1e8512892fdc7f141f91580bfca4e370b0740a56

heat/engine/resources/volume.py
heat/tests/test_volume.py

index 32564531f259dc74b90af30375780813ade37ea9..9a2adff8d93dae4dea27dd72a44eba53c1752024 100644 (file)
@@ -34,23 +34,36 @@ class Volume(resource.Resource):
                          'SnapshotId': {'Type': 'String'},
                          'Tags': {'Type': 'List'}}
 
+    _restore_property = 'SnapshotId'
+
+    def _display_name(self):
+        return self.physical_resource_name()
+
+    def _display_description(self):
+        return self.physical_resource_name()
+
+    def _create_arguments(self):
+        return {'size': self.properties['Size'],
+                'availability_zone': self.properties['AvailabilityZone']}
+
     def handle_create(self):
-        backup_id = self.properties.get('SnapshotId')
+        backup_id = self.properties.get(self._restore_property)
         cinder = self.cinder()
         if backup_id is not None:
             if volume_backups is None:
-                raise exception.Error('SnapshotId not supported')
+                raise exception.Error(
+                    '%s not supported' % self._restore_property)
             vol_id = cinder.restores.restore(backup_id)['volume_id']
 
             vol = cinder.volumes.get(vol_id)
             vol.update(
-                display_name=self.physical_resource_name(),
-                display_description=self.physical_resource_name())
+                display_name=self._display_name(),
+                display_description=self._display_description())
         else:
             vol = cinder.volumes.create(
-                self.properties['Size'],
-                display_name=self.physical_resource_name(),
-                display_description=self.physical_resource_name())
+                display_name=self._display_name(),
+                display_description=self._display_description(),
+                **self._create_arguments())
 
         while vol.status == 'creating':
             eventlet.sleep(1)
@@ -119,8 +132,46 @@ class VolumeAttachment(resource.Resource):
         self.stack.clients.detach_volume_from_instance(server_id, volume_id)
 
 
+class CinderVolume(Volume):
+
+    properties_schema = {'availability_zone': {'Type': 'String',
+                                               'Required': True},
+                         'size': {'Type': 'Number'},
+                         'snapshot_id': {'Type': 'String'},
+                         'backup_id': {'Type': 'String'},
+                         'name': {'Type': 'String'},
+                         'description': {'Type': 'String'},
+                         'volume_type': {'Type': 'String'},
+                         'metadata': {'Type': 'Map'},
+                         'imageRef': {'Type': 'String'},
+                         'source_volid': {'Type': 'String'}}
+
+    _restore_property = 'backup_id'
+
+    def _display_name(self):
+        name = self.properties['name']
+        if name:
+            return name
+        return super(CinderVolume, self)._display_name()
+
+    def _display_description(self):
+        return self.properties['description']
+
+    def _create_arguments(self):
+        arguments = {
+            'size': self.properties['size'],
+            'availability_zone': self.properties['availability_zone']
+        }
+        optionals = ['snapshot_id', 'volume_type', 'imageRef', 'source_volid',
+                     'metadata']
+        arguments.update((prop, self.properties[prop]) for prop in optionals
+                         if self.properties[prop])
+        return arguments
+
+
 def resource_mapping():
     return {
         'AWS::EC2::Volume': Volume,
         'AWS::EC2::VolumeAttachment': VolumeAttachment,
+        'OS::Cinder::Volume': CinderVolume,
     }
index 2804eb30607c36ae33a4b23fff8df49b40bc9bac..141a21aaa96d12faa093113d4f5ee0fb5eb40da6 100644 (file)
@@ -85,9 +85,9 @@ class VolumeTest(HeatTestCase):
         setup_dummy_db()
 
     def create_volume(self, t, stack, resource_name):
-        resource = vol.Volume(resource_name,
-                              t['Resources'][resource_name],
-                              stack)
+        data = t['Resources'][resource_name]
+        data['Properties']['AvailabilityZone'] = 'nova'
+        resource = vol.Volume(resource_name, data, stack)
         self.assertEqual(resource.validate(), None)
         scheduler.TaskRunner(resource.create)()
         self.assertEqual(resource.state, vol.Volume.CREATE_COMPLETE)
@@ -110,7 +110,8 @@ class VolumeTest(HeatTestCase):
         clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
             self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
 
         # delete script
@@ -150,7 +151,8 @@ class VolumeTest(HeatTestCase):
         # create script
         clients.OpenStackClients.cinder().AndReturn(self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
 
         eventlet.sleep(1).AndReturn(None)
@@ -158,6 +160,7 @@ class VolumeTest(HeatTestCase):
         self.m.ReplayAll()
 
         t = template_format.parse(volume_template)
+        t['Resources']['DataVolume']['Properties']['AvailabilityZone'] = 'nova'
         stack = parse_stack(t, stack_name=stack_name)
 
         resource = vol.Volume('DataVolume',
@@ -177,7 +180,8 @@ class VolumeTest(HeatTestCase):
         clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
             self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
 
         # create script
@@ -194,6 +198,7 @@ class VolumeTest(HeatTestCase):
         self.m.ReplayAll()
 
         t = template_format.parse(volume_template)
+        t['Resources']['DataVolume']['Properties']['AvailabilityZone'] = 'nova'
         stack = parse_stack(t, stack_name=stack_name)
 
         scheduler.TaskRunner(stack['DataVolume'].create)()
@@ -215,7 +220,8 @@ class VolumeTest(HeatTestCase):
         clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
             self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
 
         # create script
@@ -238,6 +244,7 @@ class VolumeTest(HeatTestCase):
         self.m.ReplayAll()
 
         t = template_format.parse(volume_template)
+        t['Resources']['DataVolume']['Properties']['AvailabilityZone'] = 'nova'
         stack = parse_stack(t, stack_name=stack_name)
 
         scheduler.TaskRunner(stack['DataVolume'].create)()
@@ -260,7 +267,8 @@ class VolumeTest(HeatTestCase):
         clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
             self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
         eventlet.sleep(1).AndReturn(None)
 
@@ -292,7 +300,8 @@ class VolumeTest(HeatTestCase):
         clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
             self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
         eventlet.sleep(1).AndReturn(None)
 
@@ -321,7 +330,8 @@ class VolumeTest(HeatTestCase):
         clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
             self.cinder_fc)
         self.cinder_fc.volumes.create(
-            u'1', display_description='%s.DataVolume' % stack_name,
+            size=u'1', availability_zone='nova',
+            display_description='%s.DataVolume' % stack_name,
             display_name='%s.DataVolume' % stack_name).AndReturn(fv)
         eventlet.sleep(1).AndReturn(None)
 
@@ -329,6 +339,7 @@ class VolumeTest(HeatTestCase):
 
         t = template_format.parse(volume_template)
         t['Resources']['DataVolume']['DeletionPolicy'] = 'Snapshot'
+        t['Resources']['DataVolume']['Properties']['AvailabilityZone'] = 'nova'
         stack = parse_stack(t, stack_name=stack_name)
         resource = vol.Volume('DataVolume',
                               t['Resources']['DataVolume'],
@@ -370,6 +381,85 @@ class VolumeTest(HeatTestCase):
 
         self.m.VerifyAll()
 
+    def test_cinder_create(self):
+        fv = FakeVolume('creating', 'available')
+        stack_name = 'test_volume_stack'
+
+        clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+            self.cinder_fc)
+        self.cinder_fc.volumes.create(
+            size=u'1', availability_zone='nova',
+            display_description='CustomDescription',
+            display_name='CustomName',
+            imageRef='Image1',
+            snapshot_id='snap-123',
+            metadata={'key': 'value'},
+            source_volid='vol-012',
+            volume_type='lvm').AndReturn(fv)
+
+        eventlet.sleep(1).AndReturn(None)
+
+        self.m.ReplayAll()
+
+        t = template_format.parse(volume_template)
+        t['Resources']['DataVolume']['Properties'] = {
+            'size': '1',
+            'availability_zone': 'nova',
+            'name': 'CustomName',
+            'description': 'CustomDescription',
+            'volume_type': 'lvm',
+            'metadata': {'key': 'value'},
+            # Note that specifying all these arguments doesn't work in
+            # practice, as they are conflicting, but we just want to check they
+            # are sent to the backend.
+            'imageRef': 'Image1',
+            'snapshot_id': 'snap-123',
+            'source_volid': 'vol-012',
+        }
+        stack = parse_stack(t, stack_name=stack_name)
+
+        resource = vol.CinderVolume('DataVolume',
+                                    t['Resources']['DataVolume'],
+                                    stack)
+        self.assertEqual(resource.validate(), None)
+        scheduler.TaskRunner(resource.create)()
+        self.assertEqual(resource.state, vol.Volume.CREATE_COMPLETE)
+        self.assertEqual(fv.status, 'available')
+
+        self.m.VerifyAll()
+
+    def test_cinder_default(self):
+        fv = FakeVolume('creating', 'available')
+        stack_name = 'test_volume_stack'
+
+        clients.OpenStackClients.cinder().MultipleTimes().AndReturn(
+            self.cinder_fc)
+        self.cinder_fc.volumes.create(
+            size=u'1', availability_zone='nova',
+            display_description=None,
+            display_name='%s.DataVolume' % stack_name).AndReturn(fv)
+
+        eventlet.sleep(1).AndReturn(None)
+
+        self.m.ReplayAll()
+
+        t = template_format.parse(volume_template)
+        t['Resources']['DataVolume']['Properties'] = {
+            'size': '1',
+            'availability_zone': 'nova',
+        }
+        stack = parse_stack(t, stack_name=stack_name)
+
+        resource = vol.CinderVolume('DataVolume',
+                                    t['Resources']['DataVolume'],
+                                    stack)
+        self.assertEqual(resource.validate(), None)
+        scheduler.TaskRunner(resource.create)()
+        self.assertEqual(resource.state, vol.Volume.CREATE_COMPLETE)
+        self.assertEqual(fv.status, 'available')
+
+        self.m.VerifyAll()
+
 
 class FakeVolume:
     status = 'attaching'