<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 14 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:Wingdings;
        panose-1:5 0 0 0 0 0 0 0 0 0;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";
        mso-fareast-language:EN-US;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
p.MsoPlainText, li.MsoPlainText, div.MsoPlainText
        {mso-style-priority:99;
        mso-style-link:"Plain Text Char";
        margin:0cm;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";
        mso-fareast-language:EN-US;}
span.EmailStyle17
        {mso-style-type:personal-compose;
        font-family:"Calibri","sans-serif";
        color:windowtext;}
span.PlainTextChar
        {mso-style-name:"Plain Text Char";
        mso-style-priority:99;
        mso-style-link:"Plain Text";
        font-family:"Calibri","sans-serif";}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri","sans-serif";
        mso-fareast-language:EN-US;}
@page WordSection1
        {size:612.0pt 792.0pt;
        margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="FR-CA" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal"><span lang="EN-US">Hi guys,<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">I’ve wrote simple patch that takes care of the volume deletion and it’s ugly.  I’m posting it here so anybody with better code knowledge on heat can base his patch on it if it’s very that ugly
</span><span lang="EN-US" style="font-family:Wingdings">J</span><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">For some reasons, sometimes an instance will fail to delete, this takes care of it and will retry until it disappears:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">--- instance.py.orig    2014-08-30 22:07:45.259328109 +0000<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+++ instance.py 2014-08-30 22:23:03.180527084 +0000<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">@@ -587,8 +587,13 @@<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                     self.resource_id_set(None)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                     break<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                 elif server.status == "ERROR":<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">-                    raise exception.Error(_("Deletion of server %s failed.") %<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">-                                          server.id)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                    while server.status == "ERROR":<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                        nova_utils.refresh_server(server)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                       server.reset_state()<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                       server.delete()<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                        if server.status == "DELETED":<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                            self.resource_id_set(None)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">+                            break<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">             except clients.novaclient.exceptions.NotFound:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                 self.resource_id_set(None)<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">                 break<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">This patch is based on the following bug:
<a href="https://bugs.launchpad.net/heat/+bug/1298350">https://bugs.launchpad.net/heat/+bug/1298350</a> => https://review.openstack.org/#/c/86638/<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">It didn’t work for me but this one works flawlessly.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">--- volume.py.icehouse               2014-08-30 00:59:49.844290344 +0000<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+++ volume.py 2014-08-30 21:58:08.769813146 +0000<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">@@ -151,11 +151,11 @@<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 if vol.status == 'in-use':<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                     logger.warn(_('can not delete volume when in-use'))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                     raise exception.Error(_('Volume in use'))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                vol.delete()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                while True:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                    yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                    vol.get()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                             if vol.status != 'deleting':<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                    vol.delete()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                    while True:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                        yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                        vol.get()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             except clients.cinderclient.exceptions.NotFound:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 self.resource_id_set(None)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">@@ -227,69 +227,187 @@<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         logger.info(_('%s - complete') % str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-class VolumeDetachTask(object):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+class VolumeDeleteTask(object):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     """A task for detaching a volume from a Nova server."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-    def __init__(self, stack, server_id, attachment_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def __init__(self, stack, volume_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         """<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         Initialise with the stack (for obtaining the clients), and the IDs of<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         the server and volume.<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         """<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         self.clients = stack.clients<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        self.server_id = server_id<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        self.attachment_id = attachment_id<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        self.volume_id = volume_id<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     def __str__(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         """Return a human-readable string description of the task."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        return _('Removing attachment %(att)s from Instance %(srv)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            'att': self.attachment_id, 'srv': self.server_id}<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return _('Deleting volume %(vol)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            'vol': self.volume_id}<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     def __repr__(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         """Return a brief string description of the task."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        return '%s(%s -/> %s)' % (type(self).__name__,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                                  self.attachment_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                                  self.server_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return '%s(%s)' % (type(self).__name__,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                                  self.volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     def __call__(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         """Return a co-routine which runs the task."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         logger.debug(str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        server_api = self.clients.nova().volumes<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        # get reference to the volume while it is attached<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         try:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            nova_vol = server_api.get_server_volume(self.server_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                                                    self.attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            vol = self.clients.cinder().volumes.get(nova_vol.id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        except (clients.cinderclient.exceptions.NotFound,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                clients.novaclient.exceptions.BadRequest,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                clients.novaclient.exceptions.NotFound):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            vol = self.clients.cinder().volumes.get(self.volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if vol.status in ('available'):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                             vol.delete()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            while vol.status in ('available', 'deleting'):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                logger.debug(_('%s - volume is still %s') % ( str(self), vol.status ))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                vol.get()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            logger.info(_('%(name)s - status: %(status)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                        'name': str(self), 'status': vol.status})<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        except clients.cinderclient.exceptions.NotFound:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             logger.warning(_('%s - volume not found') % str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             return<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        except clients.cinderclient.exceptions.BadRequest:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            msg = _('Failed to delete %(vol)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                'vol': self.volume_id}<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            raise exception.Error(msg)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        # detach the volume using volume_attachment<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        try:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            server_api.delete_server_volume(self.server_id, self.attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText">-        except (clients.novaclient.exceptions.BadRequest,<o:p></o:p></p>
<p class="MsoPlainText"><span lang="EN-US">-                clients.novaclient.exceptions.NotFound) as e:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            logger.warning('%(res)s - %(err)s' % {'res': str(self),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                                                  'err': str(e)})<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        logger.info(_('%s - complete') % str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+class VolumeAttachment(resource.Resource):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    PROPERTIES = (<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        INSTANCE_ID, VOLUME_ID, DEVICE,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    ) = (<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        'InstanceId', 'VolumeId', 'Device',<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    )<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    properties_schema = {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        INSTANCE_ID: properties.Schema(<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            properties.Schema.STRING,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            _('The ID of the instance to which the volume attaches.'),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            required=True,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            update_allowed=True<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        ),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        VOLUME_ID: properties.Schema(<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            properties.Schema.STRING,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            _('The ID of the volume to be attached.'),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            required=True,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            update_allowed=True<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        ),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        DEVICE: properties.Schema(<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            properties.Schema.STRING,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            _('The device where the volume is exposed on the instance. This '<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+              'assignment may not be honored and it is advised that the path '<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+              '/dev/disk/by-id/virtio-<VolumeId> be used instead.'),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            required=True,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            update_allowed=True,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            constraints=[<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                constraints.AllowedPattern('/dev/vd[b-z]'),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            ]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        ),<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    }<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    update_allowed_keys = ('Properties',)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def handle_create(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        server_id = self.properties[self.INSTANCE_ID]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        volume_id = self.properties[self.VOLUME_ID]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        dev = self.properties[self.DEVICE]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        attach_task = VolumeAttachTask(self.stack, server_id, volume_id, dev)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        attach_runner = scheduler.TaskRunner(attach_task)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        attach_runner.start()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        self.resource_id_set(attach_task.attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return attach_runner<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def check_create_complete(self, attach_runner):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return attach_runner.step()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def handle_delete(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        server_id = self.properties[self.INSTANCE_ID]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        volume_id = self.properties[self.VOLUME_ID]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        detach_task = VolumeDetachTask(self.stack, server_id, volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        scheduler.TaskRunner(detach_task)()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        delete_task = VolumeDeleteTask(self.stack,volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        scheduler.TaskRunner(delete_task)()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        checkers = []<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        if prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            volume_id = self.properties.get(self.VOLUME_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            server_id = self.properties.get(self.INSTANCE_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            detach_task = VolumeDetachTask(self.stack, server_id, volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            checkers.append(scheduler.TaskRunner(detach_task))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if self.VOLUME_ID in prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                volume_id = prop_diff.get(self.VOLUME_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            device = self.properties.get(self.DEVICE)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if self.DEVICE in prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText">+                device = prop_diff.get(self.DEVICE)<o:p></o:p></p>
<p class="MsoPlainText"><span lang="EN-US">+            if self.INSTANCE_ID in prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                server_id = prop_diff.get(self.INSTANCE_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            attach_task = VolumeAttachTask(self.stack, server_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                                           volume_id, device)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            checkers.append(scheduler.TaskRunner(attach_task))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        if checkers:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            checkers[0].start()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return checkers<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def check_update_complete(self, checkers):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        for checker in checkers:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if not checker.started():<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                checker.start()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if not checker.step():<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                return False<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        self.resource_id_set(checkers[-1]._task.attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return True<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+class VolumeDetachTask(object):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    """A task for detaching a volume from a Nova server."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def __init__(self, stack, server_id, volume_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        """<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        Initialise with the stack (for obtaining the clients), and the IDs of<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        the server and volume.<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        """<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        self.clients = stack.clients<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        self.server_id = server_id<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        self.volume_id = volume_id<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def __str__(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        """Return a human-readable string description of the task."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return _('Removing volume %(vol)s from Instance %(srv)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            'vol': self.volume_id, 'srv': self.server_id}<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def __repr__(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        """Return a brief string description of the task."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        return '%s(%s -/> %s)' % (type(self).__name__,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                                  self.volume_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                                  self.server_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+    def __call__(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        """Return a co-routine which runs the task."""<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        logger.debug(str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         try:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            vol.get()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            vol = self.clients.cinder().volumes.get(self.volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            attached_to = [att['server_id'] for att in vol.attachments]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if self.server_id not in attached_to:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                msg = _('Volume %(vol)s is not attached to server %(srv)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                    'vol': self.volume_id, 'srv': self.server_id}<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                raise exception.Error(msg)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            vol.detach()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             while vol.status in ('in-use', 'detaching'):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 logger.debug(_('%s - volume still in use') % str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                try:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                    server_api.delete_server_volume(self.server_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                                                    self.attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                except (clients.novaclient.exceptions.BadRequest,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                        clients.novaclient.exceptions.NotFound):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                    pass<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 vol.get()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             logger.info(_('%(name)s - status: %(status)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">@@ -299,27 +417,32 @@<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         except clients.cinderclient.exceptions.NotFound:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             logger.warning(_('%s - volume not found') % str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            return<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        except clients.cinderclient.exceptions.BadRequest:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            msg = _('Failed to detach %(vol)s from server %(srv)s') % {<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                'vol': self.volume_id, 'srv': self.server_id}<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            raise exception.Error(msg)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         # The next check is needed for immediate reattachment when updating:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        # as the volume info is taken from cinder, but the detach<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        # request is sent to nova, there might be some time<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        # between cinder marking volume as 'available' and<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        # nova removing attachment from it's own objects, so we<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        # there might be some time between cinder marking volume as 'available'<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        # and nova removing attachment from it's own objects, so we<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         # check that nova already knows that the volume is detached<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        def server_has_attachment(server_id, attachment_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            try:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                server_api.get_server_volume(server_id, attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            except clients.novaclient.exceptions.NotFound:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        server_api = self.clients.nova().volumes<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        def server_has_attachment(server_id, volume_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            vol = self.clients.cinder().volumes.get(self.volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            attached_to = [att['server_id'] for att in vol.attachments]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            if self.server_id not in attached_to:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 return False<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            return True<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            else:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                return True<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        while server_has_attachment(self.server_id, self.attachment_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            logger.info(_("Server %(srv)s still has attachment %(att)s.") %<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                        {'att': self.attachment_id, 'srv': self.server_id})<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        while server_has_attachment(self.server_id, self.volume_id):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            logger.info(_("Server %(srv)s still has %(vol)s attached.") %<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+                        {'vol': self.volume_id, 'srv': self.server_id})<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             yield<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        logger.info(_("Volume %(vol)s is detached from server %(srv)s") %<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                    {'vol': vol.id, 'srv': self.server_id})<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        logger.info(_('%s - complete') % str(self))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"> <o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"> class VolumeAttachment(resource.Resource):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">@@ -376,29 +499,25 @@<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     def handle_delete(self):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         server_id = self.properties[self.INSTANCE_ID]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-        detach_task = VolumeDetachTask(self.stack, server_id, self.resource_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        volume_id = self.properties[self.VOLUME_ID]<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        detach_task = VolumeDetachTask(self.stack, server_id, volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         scheduler.TaskRunner(detach_task)()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        delete_task = VolumeDeleteTask(self.stack,volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+        scheduler.TaskRunner(delete_task)()<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     def handle_update(self, json_snippet, tmpl_diff, prop_diff):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         checkers = []<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         if prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            # Even though some combinations of changed properties<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            # could be updated in UpdateReplace manner,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            # we still first detach the old resource so that<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            # self.resource_id is not replaced prematurely<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             volume_id = self.properties.get(self.VOLUME_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            server_id = self.properties.get(self.INSTANCE_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            detach_task = VolumeDetachTask(self.stack, server_id, volume_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+            checkers.append(scheduler.TaskRunner(detach_task))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">+<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             if self.VOLUME_ID in prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 volume_id = prop_diff.get(self.VOLUME_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             device = self.properties.get(self.DEVICE)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             if self.DEVICE in prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 </span>device = prop_diff.get(self.DEVICE)<o:p></o:p></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            server_id = self.properties.get(self.INSTANCE_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            detach_task = VolumeDetachTask(self.stack, server_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-                                           self.resource_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-            checkers.append(scheduler.TaskRunner(detach_task))<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             if self.INSTANCE_ID in prop_diff:<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">                 server_id = prop_diff.get(self.INSTANCE_ID)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">             attach_task = VolumeAttachTask(self.stack, server_id,<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">@@ -419,7 +538,6 @@<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         self.resource_id_set(checkers[-1]._task.attachment_id)<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">         return True<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">-<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">class CinderVolume(Volume):<o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US"><o:p></o:p></span></p>
<p class="MsoPlainText"><span lang="EN-US">     </span>PROPERTIES = (<o:p></o:p></p>
<p class="MsoNormal"><span lang="EN-US"><o:p> </o:p></span></p>
</div>
</body>
</html>