]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Make volume attachment a co-routine
authorZane Bitter <zbitter@redhat.com>
Wed, 22 May 2013 14:29:33 +0000 (16:29 +0200)
committerZane Bitter <zbitter@redhat.com>
Wed, 22 May 2013 14:29:33 +0000 (16:29 +0200)
Change-Id: Iad1ac0578470e4c2f997257560d62f8cb2d7ee35

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

index 4b0fe29d78543fc4780018aae2715191b9f16102..feb6c9cbe06f33463a870ac36ca7209468c37c57 100644 (file)
@@ -22,7 +22,6 @@ from heat.openstack.common import log as logging
 logger = logging.getLogger(__name__)
 
 
-from heat.common import exception
 from heat.common import heat_keystoneclient as hkc
 from novaclient import client as novaclient
 try:
@@ -193,24 +192,6 @@ class OpenStackClients(object):
 
         return self._cinder
 
-    def attach_volume_to_instance(self, server_id, volume_id, device_id):
-        logger.warn('Attaching InstanceId %s VolumeId %s Device %s' %
-                    (server_id, volume_id, device_id))
-
-        va = self.nova().volumes.create_server_volume(
-            server_id=server_id,
-            volume_id=volume_id,
-            device=device_id)
-
-        vol = self.cinder().volumes.get(va.id)
-        while vol.status == 'available' or vol.status == 'attaching':
-            eventlet.sleep(1)
-            vol.get()
-        if vol.status == 'in-use':
-            return va.id
-        else:
-            raise exception.Error(vol.status)
-
     def detach_volume_from_instance(self, server_id, volume_id):
         logger.info('VolumeAttachment un-attaching %s %s' %
                     (server_id, volume_id))
index 5e1bc1269da8e63599f18a93ceb5190594ccb0bb..c76782f21634c9b8f6d0b3b3b0360e0081bf5b73 100644 (file)
@@ -25,6 +25,9 @@ from oslo.config import cfg
 
 from heat.engine import clients
 from heat.engine import resource
+from heat.engine import scheduler
+from heat.engine.resources import volume
+
 from heat.common import exception
 from heat.engine.resources.network_interface import NetworkInterface
 
@@ -363,15 +366,16 @@ class Instance(resource.Resource):
     def attach_volumes(self):
         if not self.properties['Volumes']:
             return
-        server_id = self.resource_id
         for vol in self.properties['Volumes']:
             if 'DeviceId' in vol:
                 dev = vol['DeviceId']
             else:
                 dev = vol['Device']
-            self.stack.clients.attach_volume_to_instance(server_id,
-                                                         vol['VolumeId'],
-                                                         dev)
+            attach_task = volume.VolumeAttachTask(self.stack,
+                                                  self.resource_id,
+                                                  vol['VolumeId'],
+                                                  vol['Device'])
+            scheduler.TaskRunner(attach_task)()
 
     def detach_volumes(self):
         server_id = self.resource_id
index ef09f8b331f6eb8d3d48da0a3b20a5f09caffd98..8c78846e768661eca2a29d161891668d48a780ff 100644 (file)
@@ -118,6 +118,56 @@ class Volume(resource.Resource):
         return self._delete()
 
 
+class VolumeAttachTask(object):
+    """A task for attaching a volume to a Nova server."""
+
+    def __init__(self, stack, server_id, volume_id, device):
+        """
+        Initialise with the stack (for obtaining the clients), ID of the
+        server and volume, and the device name on the server.
+        """
+        self.clients = stack.clients
+        self.server_id = server_id
+        self.volume_id = volume_id
+        self.device = device
+        self.attachment_id = None
+
+    def __str__(self):
+        """Return a human-readable string description of the task."""
+        return 'Attaching Volume %s to Instance %s as %s' % (self.volume_id,
+                                                             self.server_id,
+                                                             self.device)
+
+    def __repr__(self):
+        """Return a brief string description of the task."""
+        return '%s(%s -> %s [%s])' % (type(self).__name__,
+                                      self.volume_id,
+                                      self.server_id,
+                                      self.device)
+
+    def __call__(self):
+        """Return a co-routine which runs the task."""
+        logger.debug(str(self))
+
+        va = self.clients.nova().volumes.create_server_volume(
+            server_id=self.server_id,
+            volume_id=self.volume_id,
+            device=self.device)
+        self.attachment_id = va.id
+        yield
+
+        vol = self.clients.cinder().volumes.get(self.volume_id)
+        while vol.status == 'available' or vol.status == 'attaching':
+            logger.debug('%s - volume status: %s' % (str(self), vol.status))
+            yield
+            vol.get()
+
+        if vol.status != 'in-use':
+            raise exception.Error(vol.status)
+
+        logger.info('%s - complete' % str(self))
+
+
 class VolumeAttachment(resource.Resource):
     properties_schema = {'InstanceId': {'Type': 'String',
                                         'Required': True},
@@ -135,10 +185,9 @@ class VolumeAttachment(resource.Resource):
         server_id = self.properties[self._instance_property]
         volume_id = self.properties[self._volume_property]
         dev = self.properties[self._device_property]
-        inst = self.stack.clients.attach_volume_to_instance(server_id,
-                                                            volume_id,
-                                                            dev)
-        self.resource_id_set(inst)
+        attach_task = VolumeAttachTask(self.stack, server_id, volume_id, dev)
+        scheduler.TaskRunner(attach_task)()
+        self.resource_id_set(attach_task.attachment_id)
 
     def handle_update(self, json_snippet):
         return self.UPDATE_REPLACE
index bac8c6fd9d2fbb31c6184e9b273936b184b38152..e84073d09fcbc3b4eecc9c63df89809fa58c5c7e 100644 (file)
@@ -14,6 +14,7 @@
 
 
 import eventlet
+import mox
 import json
 
 from testtools import skipIf
@@ -186,7 +187,7 @@ class VolumeTest(HeatTestCase):
         # create script
         clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc)
 
-        eventlet.sleep(1).MultipleTimes().AndReturn(None)
+        scheduler.TaskRunner._sleep(mox.IsA(int)).AndReturn(None)
         self.fc.volumes.create_server_volume(
             device=u'/dev/vdc',
             server_id=u'WikiDatabase',
@@ -225,7 +226,7 @@ class VolumeTest(HeatTestCase):
 
         # create script
         clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc)
-        eventlet.sleep(1).MultipleTimes().AndReturn(None)
+        scheduler.TaskRunner._sleep(mox.IsA(int)).AndReturn(None)
         self.fc.volumes.create_server_volume(
             device=u'/dev/vdc',
             server_id=u'WikiDatabase',
@@ -238,6 +239,7 @@ class VolumeTest(HeatTestCase):
         self.fc.volumes.delete_server_volume('WikiDatabase',
                                              'vol-123').AndReturn(None)
         self.cinder_fc.volumes.get('vol-123').AndReturn(fva)
+        eventlet.sleep(mox.IsA(int)).AndReturn(None)
 
         self.m.ReplayAll()
 
@@ -520,7 +522,7 @@ class VolumeTest(HeatTestCase):
 
         # create script
         clients.OpenStackClients.nova().MultipleTimes().AndReturn(self.fc)
-        eventlet.sleep(1).MultipleTimes().AndReturn(None)
+        scheduler.TaskRunner._sleep(mox.IsA(int)).AndReturn(None)
         self.fc.volumes.create_server_volume(
             device=u'/dev/vdc',
             server_id=u'WikiDatabase',
@@ -533,6 +535,7 @@ class VolumeTest(HeatTestCase):
         self.fc.volumes.delete_server_volume('WikiDatabase',
                                              'vol-123').AndReturn(None)
         self.cinder_fc.volumes.get('vol-123').AndReturn(fva)
+        eventlet.sleep(mox.IsA(int)).AndReturn(None)
 
         self.m.ReplayAll()