From: Zane Bitter Date: Wed, 22 May 2013 14:29:33 +0000 (+0200) Subject: Make volume attachment a co-routine X-Git-Tag: 2014.1~572^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=4c8cad7402a202e108e16451bf14cebb8ac04d67;p=openstack-build%2Fheat-build.git Make volume attachment a co-routine Change-Id: Iad1ac0578470e4c2f997257560d62f8cb2d7ee35 --- diff --git a/heat/engine/clients.py b/heat/engine/clients.py index 4b0fe29d..feb6c9cb 100644 --- a/heat/engine/clients.py +++ b/heat/engine/clients.py @@ -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)) diff --git a/heat/engine/resources/instance.py b/heat/engine/resources/instance.py index 5e1bc126..c76782f2 100644 --- a/heat/engine/resources/instance.py +++ b/heat/engine/resources/instance.py @@ -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 diff --git a/heat/engine/resources/volume.py b/heat/engine/resources/volume.py index ef09f8b3..8c78846e 100644 --- a/heat/engine/resources/volume.py +++ b/heat/engine/resources/volume.py @@ -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 diff --git a/heat/tests/test_volume.py b/heat/tests/test_volume.py index bac8c6fd..e84073d0 100644 --- a/heat/tests/test_volume.py +++ b/heat/tests/test_volume.py @@ -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()